1

A little bit of background: Flutter noob here, trying to build an Add To Favourites function. The list of items sits in a PHP/MySQL database which I access using http on FirstScreen. When user taps on one of the list items, they get redirected to SecondScreen and I pass the 'itemId' using MaterialPageRoute, which has an Add To Favourites button. The 'itemId' of favourited items are stored locally using sqflite. I can access 'itemId' using a StatefulWidget but I haven't been successful trying to create a custom Stateful widget that manages a Stateless Widget, like in this Flutter example on interactivity, i.e. I am unable to access 'itemId' from the custom Stateful widget.

Code snippet:

MaterialPageRoute(
  builder: (context) => SecondScreen(
  itemId: item[index].itemId,

My sqflite checkFavourited function:

Future<bool> checkFavourited(String itemId) async {
        Database db = await instance.database;
        var bookmarked = await db.query(table,
            where: 'itemId = ?',
            whereArgs: [itemId]);
        return bookmarked.isEmpty ? false : true;
      }

Working StatefulWidget:

 bool _isFavourited = false;

  void isFavourited() async {
    final isFavourited =
        await DatabaseHelper.instance.checkFavourited(widget.itemId);
    setState(() {
      _isFavourited = isFavourited;
    });
  }

But my attempts to do it in a StatelessWidget have failed. So far I have:

class SecondScreen extends StatelessWidget {
  final String itemId;
  SecondScreen({
    Key key,
    @required this.itemId,

I get the following error message:

The getter 'itemId' isn't defined for the type 'FavouritedButton'.

when I try to do this:

class FavouritedButton extends StatefulWidget {
  @override
  _FavouritedButtonState createState() => _FavouritedButtonState();
}

class _FavouritedButtonState extends State<FavouritedButton> {
  bool _isFavourited = false;

  void isFavourited() async {
    final isFavourited =
    await DatabaseHelper.instance.checkBookmark(widget.itemId);
    setState(() {
      _isFavourited = isFavourited;
    });
  }
  @override
  Widget build(BuildContext context) {
    return IconButton(
      icon: isFavourited
          ? Icon(Icons.bookmark)
          : Icon(
              Icons.bookmark_border,
            ),

I have read Flutter's Simple app state management and List of state management approaches articles and I might be wrong but is there be an easier solution than Provider or FutureBuilder?

I know this is a long post and will really appreciate if anyone can give me some pointers on this. Thanks in advance!

keymistress
  • 138
  • 9

2 Answers2

1

Clean code approach, by isolating your widget rebuild to be as narrow as possible, you are doing things right, but need to move id to your stateful widget.

class FavouritedButton extends StatefulWidget {
final String itemId;
  FavouritedButton({required this.itemId});

  @override
  _FavouritedButtonState createState() => _FavouritedButtonState();
}
class _FavouritedButtonState extends State<FavouritedButton> {
//add initState here, and keep the rest of your widget as it is.
 @override
  void initState() {
  isFavourited();
    super.initState();
  }

Then in your stateless widget:

class SecondScreen extends StatelessWidget {
 final String itemId;

  const SecondScreen({Key? key, required this.itemId}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    return Column(children:[
     Text('write some text about your widget or $itemId'),
     FavouritedButton(itemId:itemId)
  ]);
  }
}

This will only rebuild your favorite button.

Huthaifa Muayyad
  • 11,321
  • 3
  • 17
  • 49
  • Worked like a charm! Two questions: I added a dispose function after initState and Key key : super(key: key) for the required 'itemId' in the StatefulWidget, are they necessary? – keymistress Aug 06 '21 at 11:48
  • 1
    Key's are good only if you use them, they aren't required, but preferred, as your widgets get more complex, keys will enhance performance. As for dispose, it's already built into the widget when it's removed, like initstate, but if you aren't adding any logic in dispose, there is no need to write, and in your widgets, you don't have anything to dispose like streams or listeners or focus nodes. – Huthaifa Muayyad Aug 06 '21 at 12:55
  • What the benefit of using MaterialPageRoute and passing the data in Settings ? I had to use this, because I have to InitBloc with the passed data, and i could not do it passing the data in Settings. – paakjis Sep 19 '22 at 15:10
0

According to your code snippet, you are passing data using Construstor. But you didn't declare itemId inside FavouritedButton. As @Huthaifa Muayyad said, it will be solved. If you like to pass data as route arguments, you need to pass like this

  Navigator.of(context).push(
                  MaterialPageRoute(
                      builder: (context) => FavouritedButton (),
                      settings: RouteSettings(
                        arguments: itemId,
                      )),
                );

TO receive on widgetSide

 Widget build(BuildContext context) {
    final data = ModalRoute.of(context)!.settings;

I describe more here

Md. Yeasin Sheikh
  • 54,221
  • 7
  • 29
  • 56