How to solve ‘Tried to use Provider with a subtype of Listenable-Stream’ Provider error in Flutter

How to solve ‘Tried to use Provider with a subtype of Listenable/Stream’ Provider error in Flutter

Provider is one of the most popular package of Flutter and you may come across the error “Tried to use Provider with a subtype of Listenable/Stream” while using in an application.

The Provider package may also recommend you a solution like this –

This is likely a mistake, as Provider will not automatically update dependents
when DataProvider is updated. Instead, consider changing Provider for more specific
implementation that handles the update mechanism, such as:

– ListenableProvider
– ChangeNotifierProvider
– ValueListenableProvider
– StreamProvider

There are couple of ways to solve this error depending on how you want to use the package.

Solution 1:

This is according to the message suggested from the Provider package.

If you want to use the Provider to access some data and also wants to update the state of an widget whenever the state changes  then instead of using Provider in the main.dart file (usually the provider is injected/provided here) you can use any of the suggested provider such as ListenableProvider.

example:

in main.dart file

void main() {
  runApp(
      ListenableProvider(create: (context) => DataProvider(),child: MyApp(), )
  );
}

Instead of ListenableProvider, you can use any other suggested provider depending on your application requirement.

Solution 2:

If you want to use your provider only to access some data across the application which never changes throughout the application, then you can make your class (provider class) do not extend ChangeNotifier

example:

import 'package:flutter/material.dart';

class DataProvider {
  final String imgSource = 'https://example.com/assets/images';  
}

Now you can use Provider class inside the main.dart file to provide the provider.

How to Change - Update the State inside Bottom Sheet in Flutter

How to Change / Update the State inside Bottom Sheet in Flutter

Bottom Sheets are one of the commonly used things in a mobile application. We can use them for different types of requirements. If we want to display some information to the user such as more details about something, then it can be a good place.

If we want to keep some settings inside the Bottom Sheet such as a check box, then handling the state of this check box can be tricky.

example:

bool toggleIcon = true;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Container(
        child: Center(
          child: Column(
            children: [
              Icon(
                  toggleIcon ? Icons.check_box : Icons.check_box_outline_blank
              ),
              ElevatedButton(onPressed: () {
                showModalBottomSheet(
                    context: context,
                    builder: ( BuildContext context ) {
                      return Container(
                        color: Colors.blue,
                        child: Column(
                          children: [
                            IconButton(onPressed:  () {
                              setState(() {
                                toggleIcon= !toggleIcon;
                              });
                            } ,
                                icon: Icon(
                                    toggleIcon ? Icons.check_box : Icons.check_box_outline_blank
                                ) )
                          ],
                        ),
                      );
                    }
                );
              } , child: Text('Show Bottom Sheet'))
            ],
          ),
        ),
      ),
    ) ;
  }

In this example, we are showing bottom sheet which contains an icon button which changes the state of a property when we click on it. When we run this code and clicks on the icon button, then we can see that the icon which is inside the main body changes but not the icon which is inside the bottom sheet.

The reason is, when we click on the icon inside the bottom sheet, we are changing the state of the main widget but not the state of the bottom sheet. When we close the bottom bar and open it again, then we pass the updated context so we can find the updated icon.

Here, if you want, what you can do is, when the user clicks on the icon button inside the bottom sheet, after setting the state, you can immediately pop the bottom sheet by using Navigator.pop(context).

By Using StatefulBuilder

If you are concerned only about the state of the bottom sheet, and do not want to reflect any changes in the calling widget, you can use StatefulBuilder widget.

example:

replace the showModalBottomSheet from the previous example with this one

showModalBottomSheet(
                    context: context,
                    builder: ( BuildContext context ) {
                      return StatefulBuilder(
                        builder: (BuildContext context, StateSetter setState) {
                          return Container(
                            color: Colors.blue,
                            child: Column(
                              children: [
                                IconButton(onPressed:  () {
                                  setState(() {
                                    toggleIcon= !toggleIcon;
                                  });
                                } ,
                                    icon: Icon(
                                        toggleIcon ? Icons.check_box : Icons.check_box_outline_blank
                                    ) )
                              ],
                            ),
                          );
                        } ,
                      );
                    }
                )

With this code, we are giving the bottom sheet, its own state. When you run the code with this example, now when you click on the icon button inside bottom sheet, you can find that the icon toggles inside the bottom sheet, but the one inside the body stays same.

Bottom Sheet as Stateful Widget

In-order to change the state inside the bottom sheet as well as the widget from which it is called, we can make a stateful widget and use it as a bottom sheet. We also need to pass a callback function to this widget which triggers setState in the parent widget.

example:

import 'package:flutter/material.dart';
class BottomSheetState extends StatefulWidget {
  @override
  _BottomSheetStateState createState() => _BottomSheetStateState();
}

class _BottomSheetStateState extends State<BottomSheetState> {
  bool toggleIcon = true;
  toggleIconState(bool value ) {
    setState(() {
      toggleIcon = value;
    });
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Container(
        child: Center(
          child: Column(
            children: [
              Icon(
                  toggleIcon ? Icons.check_box : Icons.check_box_outline_blank
              ),
              ElevatedButton(onPressed: () {
                showModalBottomSheet(
                    context: context,
                    builder: ( BuildContext context ) {
                      return StatefulBottomSheet(toggleIcon: toggleIcon, valueChanged: toggleIconState,);
                    }
                );
              } , child: Text('Show Bottom Sheet'))
            ],
          ),
        ),
      ),
    ) ;
  }
}

class StatefulBottomSheet extends StatefulWidget {
  final bool toggleIcon;
  final ValueChanged<bool> valueChanged;
  StatefulBottomSheet( {Key? key, required this.toggleIcon, required this.valueChanged } );

  @override
  _StatefulBottomSheetState createState() => _StatefulBottomSheetState();
}

class _StatefulBottomSheetState extends State<StatefulBottomSheet> {
  late bool _toggleIcon;
  @override
  void initState() {
    _toggleIcon = widget.toggleIcon;
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return  Container(
      color: Colors.blue,
      child: Column(
        children: [
          IconButton(onPressed:  () {
            setState(() {
              _toggleIcon= !_toggleIcon;
            });
            widget.valueChanged(_toggleIcon);
          } ,
              icon: Icon(
                  _toggleIcon ? Icons.check_box : Icons.check_box_outline_blank
              ) )
        ],
      ),
    );
  }
}

By using Provider

Another way to achieve this functionality is by using Provider package. We can now create a provider and move all the properties and logic to it and add it to the main.dart file.

import 'package:flutter/material.dart';

class DataProvider extends ChangeNotifier {
  bool toggleIcon = true;

  toggleIconState() {
    toggleIcon = !toggleIcon;
    notifyListeners();
  }
}

We can now use this provider to change the state inside the widget as well as in the bottom sheet.

@override
  Widget build(BuildContext context) {
    DataProvider dataProvider = Provider.of<DataProvider>(context);
    return Scaffold(
      appBar: AppBar(),
      body: Container(
        child: Center(
          child: Column(
            children: [
              Icon(
                  dataProvider.toggleIcon ? Icons.check_box : Icons.check_box_outline_blank
              ),
              ElevatedButton(onPressed: () {
                showModalBottomSheet(
                    context: context,
                    builder: ( BuildContext context ) {
                      return Container(
                        color: Colors.blue,
                        child: Column(
                          children: [
                            IconButton(onPressed:  () {
                              Provider.of<DataProvider>(context, listen: false).toggleIconState();
                            } ,
                                icon: Icon(
                                    Provider.of<DataProvider>(context,).toggleIcon ? Icons.check_box :
                                    Icons.check_box_outline_blank
                                ) )
                          ],
                        ),
                      );
                    }
                );
              } , child: Text('Show Bottom Sheet'))
            ],
          ),
        ),
      ),
    ) ;
  }

Make sure that you have added the Provider dependency in the pubspec file.

How to Solve 'No Scaffold widget found' error in Flutter when using showBottomSheet

How to Solve ‘No Scaffold widget found’ error in Flutter when using showBottomSheet

When you try to use showBottomSheet in a Flutter application, then you may come across the error message ‘No Scaffold widget found’ with an additional info such as

WidgetName widgets require a Scaffold widget ancestor.

The specific widget that could not find a Scaffold ancestor was: WidgetName

Example code that gives the error can be like this

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Container(
        child: Center(
          child: Column(
            children: [
              ElevatedButton(onPressed: () {
                showBottomSheet(
                    context: context,
                    builder: (BuildContext context) {
                      return Container(
                        color: Colors.blue,
                        child: Column(
                          children: [
                            Text('Bottom Sheet')
                          ],
                        ),
                      );
                    } );
              } , child: Text('Show Bottom Sheet'))
            ],
          ),
        ),
      ),
    ) ;
  }

The reason for getting this error is, the showBottomSheet requires the context of a Scaffold widget. The context we passed to this is not the context of the Scaffold widget but of the entire Widget itself.

Solution for ‘No Scaffold widget found’ error in Flutter when using showBottomSheet

Solution 1:

If you are not very specific about the type of widget that you are using, then instead of showBottomSheet , you can use showModalBottomSheet.

Solution 2:

If you want to use only showBottomSheet, then as the error says, we need to pass the context of the Scaffold widget to this function. To do this, we can generate a global key for Scaffold widget and run the showBottomSheet function on its context.

example:

final scaffoldKey = GlobalKey<ScaffoldState>();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: scaffoldKey,
      appBar: AppBar(),
      body: Container(
        child: Center(
          child: Column(
            children: [
              ElevatedButton(onPressed: () {
                scaffoldKey.currentState!.
                showBottomSheet((context) {
                  return Container(
                    color: Colors.blue,
                    child: Column(
                      children: [
                        Text('Bottom Sheet')
                      ],
                    ),
                  );
                });
              } , child: Text('Show Bottom Sheet'))
            ],
          ),
        ),
      ),
    ) ;
  }
How to Add Opacity - Color Filter to an Image in Flutter

How to Add Opacity / Color Filter to an Image in Flutter

We can add Opacity or Color filters to images in Flutter in many different ways.

Flutter comes with a built-in widget Opacity. Though we can use this widget to add opacity to an image, it is not recommended. We need to use this Opacity widget only when there is no other way to add opacity to an image. If we must have to do it, we can do like this

Opacity(opacity: 0.8, child: Image.asset(‘assets/sample1.jpg’, ),),

Instead of using the Opacity widget, we can use the color property of Image.asset / Image.network widget to add opacity or a filter to an image.

example:

child: Column(
            children: [
              Text('Working with Images'),
              Image.asset('assets/sample1.jpg', color: Colors.white.withOpacity(0.8), colorBlendMode: BlendMode.modulate, )
            ],
          ),

 

Output for the previous code – Without color and with color property

Without color option

With color option

Another way to add opacity or color filters to an image is to wrap it inside a Container widget and use the decoration property of the Container to add the colors and filters.

example code which is equivalent to the previous:

Container(
                height: 600,
                decoration: BoxDecoration(
                  image: DecorationImage(
                    image: AssetImage('assets/sample1.jpg'),
                    colorFilter: ColorFilter.mode(Colors.white.withOpacity(0.8), BlendMode.modulate,)
                  )
                ),
              )

 

How to Add Border to an Image in Flutter

How to Add Border to an Image in Flutter

Images are very important in most of the applications and many applications are based on only images. Presenting the images beautifully gives a very good user experience.

The default widgets we use to display the images Image.asset or Image.network do not have border property. To display border around an image, we can use a container.

example:

body: Center(
        child: Column(
          children: [
            const Text('Working with Images'),
            const Divider(height: 1,),
            Container(
              height: 300,
              margin: const EdgeInsets.all(20),
              decoration: BoxDecoration(
                border: Border.all(color: Colors.blue, width: 10 ),
              ),
              child: Image.asset('assets/Sample_3.jpg'),
            )
          ],
        ),
      ),

In the above example we used to container and used its decoration property to design a border around an image. In this example,t he image is a child to the Container widget. Instead of this, we can also use image property of decoration. When we use this, then the image works like a background image for the Container. If there is no child to Container, then we see only the image.

example:

Container(
              height: 300,
              margin: const EdgeInsets.all(20),
              decoration: BoxDecoration(
                image: const DecorationImage(
                  image: AssetImage('assets/Sample_3.jpg'),
                  fit: BoxFit.cover
                ),
                border: Border.all(color: Colors.blue, width: 10 ),
              ),
            )

Adding Curved / Circle Border to Image in Flutter

We can use the borderRadius property of  decoration to add a border to the image.

example:

Container(
              height: 300,
              margin: const EdgeInsets.all(20),
              decoration: BoxDecoration(
                image: const DecorationImage(
                  image: AssetImage('assets/Sample_3.jpg'),
                  fit: BoxFit.cover
                ),
                border: Border.all(color: Colors.blue, width: 10 ),
                borderRadius: BorderRadius.circular(30)
              ),
            )

We can remove the border property in the previous example to show only the image with curves and no border color.

example:

Container(
              height: 300,
              margin: const EdgeInsets.all(20),
              decoration: BoxDecoration(
                image: const DecorationImage(
                  image: AssetImage('assets/Sample_3.jpg'),
                  fit: BoxFit.cover
                ),
                borderRadius: BorderRadius.circular(30)
              ),
            )

Note that, if you make your Image, a child of container, then the image may overlap with border or border radius and you may not see the effect.

By Clipping using ClipRRect & ClipOval

We can use the ClipRRect widget to clip an image by giving borderRadius property to the widget.

example:

Column(
          children: [
            const Text('Working with Images'),
            const Divider(height: 1,),
            ClipRRect(
              borderRadius: BorderRadius.circular(50),
              child: Image.asset('assets/Sample_3.jpg',),
            )
          ],
        ),

Instead of ClipRRect, where we can pass radius of each corner, we can also use another widget ClipOval.

example:

Column(
          children: [
            const Text('Working with Images'),
            const Divider(height: 1,),
            ClipOval(
              child: Image.asset('assets/Sample_3.jpg', ),
            )
          ],
        ),

If the image is small or an icon, we can use CircleAvatar widget also. We mostly use this widget to display small images.

These are some of the ways to make an image have a border and border radius and definitely not the Only ways.

How to Add Background Image to AppBar in Flutter

How to Add Background Image to AppBar in Flutter

Flutter allows developers to change the background color of an app bar easily by providing backgroundColor option. Instead of background color, we may want to add a background image to the app bar.

To add the background image to AppBar widget, we can use the flexibleSpace option of the widget. The flexibleSpace property of AppBar accepts a widget as its value, so we can pass a Container widget as its value and set an image background to the Container.

example:

appBar: AppBar(
        title: Text('How to Flutter', style: TextStyle(
            color: Colors.white,
            fontSize: 28
        ),) ,
        centerTitle: true,
        flexibleSpace: Container(
          decoration: BoxDecoration(
              image: DecorationImage(
                  image: AssetImage('assets/sample.png'),
                  fit: BoxFit.fill
              )
          ),
        ),

      ),

In this way we can add a background image to an AppBar in Flutter.

How to Make Body Background Image apply to AppBar also ?

Other requirement similar to this can be like, you want to make your body’s background image extends to AppBar also. We can achieve that by making the background color of AppBar transparent and using the extendBodyBehindAppBar option of the Scaffold widget and set its value to true.

Also make sure that you have set the value of elevation to 0 in AppBar, since its default value is not zero and you may find a small shadow between the Body and AppBar if its value is not zero. If you want a separation between the app bar and the body, then you can play with this elevation property and set its value accordingly.

example:

Scaffold(
      extendBodyBehindAppBar: true,
      appBar: AppBar(
        title: Text('How to Flutter', style: TextStyle(
            color: Colors.white,
            fontSize: 28
        ),) ,
        elevation: 0,
        backgroundColor: Colors.transparent,
      ),
      body: Container(
        decoration: BoxDecoration(
          image: DecorationImage(
            image: AssetImage('assets/bgimg.jpg'),
            fit: BoxFit.fill
          )
        ),
      ),
    )

 

How to add Gradient Background Color to AppBar Widget in Flutter

How to add Gradient Background Color to AppBar Widget in Flutter

AppBar widget in Flutter comes with a background color option which takes a solid color as its value. By default we cannot add gradient color to AppBar using the available background color option.

One way to add gradient color to the AppBar is by using its flexibleSpace property. The flexibleSpace property of the AppBar accepts a widget as its value, so we can assign a container to it which itself implements a gradient background color.

example:

...
appBar: AppBar(
          title: Text('How to Flutter'),
          centerTitle: true,
          flexibleSpace: Container(
            decoration: BoxDecoration(
                gradient: LinearGradient(
                    begin: Alignment.centerLeft,
                    end: Alignment.centerRight,
                    colors: <Color>[Colors.purple, Colors.blue])),
          )),
...

The output for the above code

Another way to implement the gradient background color for the AppBar is to mock it with other widgets. That is, we can remove the appBar option from the Scaffold widget and have to design other widgets which then looks and functions like AppBar.

How to Customize the Back Button in AppBar Widget in Flutter

How to Customize the Back Button in AppBar Widget in Flutter

Often we may want to customize the back button which gets added to any page’s app bar automatically.

Changing the Color of the Back Button Icon

The default color of the back button icon, which Flutter adds to any page’s app bar when pushed onto another page is white. If you want to change only the color and not the icon itself, then we can change the color in couple of ways.

1. Using the leading option

The leading option of AppBar accepts a widget as its value. We can pass the BackButton widget as the value and set its color to any desired color.

example:

Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('How to Flutter'),
        backgroundColor: Colors.green,
        leading: BackButton(
          color: Colors.black,
        ),

      ),
    ) ;
  }
2. Using the iconTheme option of AppBar

We can use the iconTheme option of AppBar widget which accepts IconThemeData as its value and sets the color to any desired color.

example:

Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('How to Flutter'),
        backgroundColor: Colors.green,
        iconTheme: IconThemeData(
          color: Colors.redAccent
        ),
      ),
    ) ;
  }

Changing the Back Button Icon in AppBar

We can also change the default back button to any other icon. Though we can use an image or even text also in place of the back button, mostly we may be interested to have an icon.

When we replace the default back button to any other widget, we also needs to implement the default functionality of the back button (popping the page)  by ourselves.

example:

AppBar(
        title: Text('How to Flutter'),
        backgroundColor: Colors.green,
        leading: GestureDetector(
          child: Icon( Icons.arrow_back_ios, color: Colors.black,  ),
          onTap: () {
            Navigator.pop(context);
          } ,
        ) ,
      ),

Instead of using GestureDetector, we can also use any other widget, for example IconButton.

Read more on AppBar in Flutter : AppBar

How to Change the Background Color of AppBar widget in Flutter

How to Change the Background Color of AppBar widget in Flutter

In most of the Flutter applications we may not want to use the default colors and want to use app/ theme specific colors. AppBar is one of the widget which gets a good amount of attention and changing its background color can have a good impact on user experience.

The background color of AppBar widget can be changed in a couple of ways in Flutter.

  1. By modifying global theme
  2. Providing a backgroundColor option at widget level

Modifying Global Theme

We can modify the global theme of a Flutter application inside main.dart file in MaterialApp widget. We can give provide a different color to the primarySwatch option than the default color which is blue.

example:

Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.pink ,
      ),
      home: AppBarHowTo() ,
    );
  }

At AppBar Widget level

The global colors can be modified at a widget level. Though the way of modifying global colors is different from widget to widget, it is a straight forward process for AppBar background color. The AppBar accepts a backgroundColor option whose value needs to be a Color.

example:

Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('How to Flutter') ,
        centerTitle: true,
        backgroundColor: Colors.green,
      ),
    ) ;
  }

Read more about AppBar here: AppBar – A Deep Dive