Flutter - Mark as Favorite Feature - GeeksforGeeks

22 Feb, 2022
Adding to favorites is a prevalent feature in many applications. It enables the users to mark or save images, addressed, links or others stuff for easy future reference. In this article, we are going to see how to implement favorites or add to favorites feature in a flutter application. This article list two methods to do so. In the first method, we will add a simple (stateless widget) icon that changes color on tap, to mark a card for future reference. In the second example, we will be implementing a comparatively complex favorite feature which will involve StateFul widgets, saving the data in a set, and then displaying it on another screen.
1. In the first method we are going to add the heart-shaped button on the card, which will change its color on tap, to mark the card as a favorite or not favorite.
The first thing that we need to do it to make a beautiful Card. You can take a look at this article to understand how the flutter Card widget is used.
Dart
//Code snippet of a card widget//  /** Card Widget **/        child: Card(          elevation: 50,          shadowColor: Colors.black,          color: Colors.greenAccent[100],          child: SizedBox(            width: 300,            height: 500,            child: Padding(              padding: const EdgeInsets.all(20.0),              child: Column(                children: [                  CircleAvatar(                    backgroundColor: Colors.green[500],                    radius: 108,                    child: CircleAvatar(                      backgroundImage: NetworkImage(                          "https://pbs.twimg.com/profile_images/1304985167476523008/QNHrwL2q_400x400.jpg"),                           //NetworkImage                      radius: 100,                    ), //CircleAvatar                  ), //CircleAvatar                  SizedBox(                    height: 10,                  ), //SizedBox                  Text(                    'GeeksforGeeks',                    style: TextStyle(                      fontSize: 30,                      color: Colors.green[900],                      fontWeight: FontWeight.w500,                    ), //Textstyle                  ), //Text                  SizedBox(                    height: 10,                  ), //SizedBox                  Text(                    'GeeksforGeeks is a computer science portal                    for geeks at geeksforgeeks.org. It contains                    well written, well thought and well explained                    computer science and programming articles,                    quizzes, projects, interview experienxes                    and much more!!',                    style: TextStyle(                      fontSize: 15,                      color: Colors.green[900],                    ), //Textstyle                  ), //Text                  SizedBox(                    height: 10,                  ), //SizedBox                  SizedBox(                    width: 80,                    child: RaisedButton(                      onPressed: () => null,                      color: Colors.green,                      child: Padding(                        padding: const EdgeInsets.all(4.0),                        child: Row(                          children: [                            Icon(Icons.touch_app),                            Text('Visit'),                          ],                        ), //Row                      ), //Padding                    ), //RaisedButton                  ) //SizedBox                ],              ), //Column            ), //Padding          ), //SizedBox        ), //Card
This is the code snippet of a card widget which will look like this. For complete more information on the same, refer to this article.
Now we will make the screen scrollable by wrapping the body of the flutter app with SingleChild ScrollView and create another card below the first one separated by a SizedBox.  
Dart
//*Code snippet of the body*// body: SingleChildScrollView(       padding: EdgeInsets.only(left: 0, right: 0, top: 20, bottom: 20),       child: Center(                   /** Card Widget **/         child: Column(           children: [             //Card 1             Card(             ...       ),//Card 1    SizedBox(               height: 20,             ),             Card(             ...      ),//Card     ), //Center   ), //Scaffold
And after doing all this we need, implement the favourite feature.
The body of the above app contains two Cards in a SingleChildScrollView. Now to apply the above shown favourite feature we need to add the below code in the pubspec.yaml file.
dependencies:
 favorite_button: ^0.0.3
This will add the favourite button package to our app. This package is a library which allows developers to implement heart or star-shaped favourites button with animation in out flutter application.
Using this package is very simple the code for the heart-shaped button with the button being already selected is this:
FavouriteButton(
           isFavorite: true,
    valueChanged: (_isFavourite) {
      print('Is Favourite $_isFavourite)');
      },
     ),
And in case if we want the button to be unselected we can set the isFavourite parameter to false. 
This is the final code of the app.
Dart
import 'package:flutter/material.dart';import 'package:favorite_button/favorite_button.dart'; // importing dependenciesvoid main() {  runApp(           /**Our App Widget Tree Starts Here**/      MaterialApp(    home: Scaffold(      appBar: AppBar(        title: Text('GeeksforGeeks'),        backgroundColor: Colors.greenAccent[400],        centerTitle: true,      ), //AppBar      body: SingleChildScrollView(        padding: EdgeInsets.only(left: 0, right: 0, top: 20, bottom: 20),        child: Center(                     /** Card Widget **/          child: Column(            children: [              //Card 1              Card(                elevation: 50,                shadowColor: Colors.black,                color: Colors.greenAccent[100],                child: SizedBox(                  width: 310,                  height: 510,                  child: Padding(                    padding: const EdgeInsets.all(20.0),                    child: Column(                      children: [                        CircleAvatar(                          backgroundColor: Colors.green[500],                          radius: 108,                          child: CircleAvatar(                            backgroundImage: NetworkImage(                                "https://pbs.twimg.com/profile_images/1304985167476523008/QNHrwL2q_400x400.jpg"),                            //NetworkImage                            radius: 100,                          ), //CircleAvatar                        ), //CircleAvatar                        SizedBox(                          height: 10,                        ), //SizedBox                        Text(                          'GeeksforGeeks',                          style: TextStyle(                            fontSize: 30,                            color: Colors.green[900],                            fontWeight: FontWeight.w500,                          ), //Textstyle                        ), //Text                        SizedBox(                          height: 10,                        ), //SizedBox                        Text(                          'GeeksforGeeks is a computer science portalfor geeks                          at geeksforgeeks.org. It contains well written,                          well thought and well explained computer science                          and programming articles, quizzes, projects,                          interview experienxes and much more!!',                          style: TextStyle(                            fontSize: 15,                            color: Colors.green[900],                          ), //Textstyle                        ), //Text                        SizedBox(                          height: 10,                        ), //SizedBox                        Row(                          mainAxisAlignment: MainAxisAlignment.center,                          children: [                            SizedBox(                              width: 100,                              child: RaisedButton(                                onPressed: () => null,                                color: Colors.green,                                child: Padding(                                  padding: const EdgeInsets.all(4.0),                                  child: Row(                                    children: [                                      Icon(Icons.touch_app),                                      Text('Visit'),                                    ],                                  ), //Row                                ), //Padding                              ), //RaisedButton                            ),                                                         // Favourite Button                            FavoriteButton(                              isFavorite: false,                              valueChanged: (_isFavorite) {                                print('Is Favorite : $_isFavorite');                              },                            ),                          ],                        ), //SizedBox                      ],                    ), //Column                  ), //Padding                ), //SizedBox              ),              SizedBox(                height: 20,              ),                             // Card 2              Card(                elevation: 50,                shadowColor: Colors.black,                color: Colors.yellowAccent[100],                child: SizedBox(                  width: 310,                  height: 510,                  child: Padding(                    padding: const EdgeInsets.all(20.0),                    child: Column(                      children: [                        CircleAvatar(                          backgroundColor: Colors.yellow[700],                          radius: 108,                          child: CircleAvatar(                            backgroundImage: NetworkImage(                                "https://pbs.twimg.com/profile_images/1304985167476523008/QNHrwL2q_400x400.jpg"),                            //NetworkImage                            radius: 100,                          ), //CircleAvatar                        ), //CircleAvatar                        SizedBox(                          height: 10,                        ), //SizedBox                        Text(                          'GeeksforGeeks',                          style: TextStyle(                            fontSize: 30,                            color: Colors.yellow[900],                            fontWeight: FontWeight.w500,                          ), //Textstyle                        ), //Text                        SizedBox(                          height: 10,                        ), //SizedBox                        Text(                          'GeeksforGeeks is a computer science portalfor geeks                          at geeksforgeeks.org. It contains well written,                          well thought and well explained computer science                          and programming articles, quizzes, projects,                          interview experienxes and much more!!',                          style: TextStyle(                            fontSize: 15,                            color: Colors.yellow[900],                          ), //Textstyle                        ), //Text                        SizedBox(                          height: 10,                        ), //SizedBox                        Row(                          mainAxisAlignment: MainAxisAlignment.center,                          children: [                            SizedBox(                              width: 100,                              child: RaisedButton(                                onPressed: () => null,                                color: Colors.yellow[600],                                child: Padding(                                  padding: const EdgeInsets.all(4.0),                                  child: Row(                                    children: [                                      Icon(Icons.touch_app),                                      Text('Visit'),                                    ],                                  ), //Row                                ), //Padding                              ), //RaisedButton                            ),                                                         // Favourite Button                            FavoriteButton(                              isFavorite: true,                              valueChanged: (_isFavorite) {                                print('Is Favorite : $_isFavorite');                              },                            ),                          ],                        ), //SizedBox                      ],                    ), //Column                  ), //Padding                ), //SizedBox              ),            ],          ), //Card        ),      ), //Center    ), //Scaffold  ) //MaterialApp      );}
Output:
2. This second example is a bit more complex compared to the previous one. In this application is a word-pair generator.  In front of each word-pair, there is an add icon which changes to a green colored check icon whenever tapped, and in addition to the that the word-pair also gets saved in another screen.
 In this flutter application, we are generating random word-pairs on the list tile. The flutter package that we are using to get random English words is given below. The code given below needs to be added in the pubspec.yaml file in the dependencies section.
 english_words: ^3.1.5
Getting started:
Dart
import 'package:flutter/material.dart';import 'package:english_words/english_words.dart'; //importing dependenciesvoid main() => runApp(MyApp()); //inictating app buildclass MyApp extends StatelessWidget {  @override  Widget build(BuildContext context) {    return MaterialApp(      theme: ThemeData(primaryColor: Colors.green),      home: RandWords(),      debugShowCheckedModeBanner: false,    );  }}
The above code snippet is going to import dependencies (material design library and English word library) in our main.dart file and initiate the app build. The app that will be built is a material app and in the last line, the debug banner is set to disappear. 
Dart
class RandWords extends StatefulWidget {  @override  RandWordsState createState() => RandWordsState();} class RandWordsState extends State<RandWords> {  final _randomWordPairs = <WordPair>[];  final _addWordPairs = Set<WordPair>();   Widget _buildList() {    return ListView.builder(      padding: const EdgeInsets.all(16.0),      itemBuilder: (context, item) {        if (item.isEven) return Divider();         final index = item ~/ 2;         if (index >= _randomWordPairs.length) {          _randomWordPairs.addAll(generateWordPairs().take(10));        }         return _buildRow(_randomWordPairs[index]);      },    );  }
In the above code, the class RandWordsState is maintaining the state of the RandWords class. Now the RandWordsState class will take in charge of most the app logic.  In the RandWordsState class, we have two lists the first one for the random word-pair and the second one to save the word-pairs. After that in the build method, we are generating a ListView widget. The itemBuilder function is called for every word generated and if it is even the ListView widget gets separated by a divider. After that, we are pairing two random words to make a word-pair and when we reach the end of the words then ten new word-pairs are generated each time.
Let’s see how the favorite feature works.
Dart
Widget _buildRow(WordPair pair) {  final alreadyadd = _addWordPairs.contains(pair);     // word-pair tile  return ListTile(      title: Text(pair.asPascalCase, style: TextStyle(fontSize: 18.0)),      trailing: Icon(alreadyadd ? Icons.check : Icons.add,          color: alreadyadd ? Colors.green : null),      onTap: () {        setState(() {          if (alreadyadd) {            _addWordPairs.remove(pair);          } else {            _addWordPairs.add(pair);          }        });      });      }
 The above code snippet generated the list tiles for the word-pairs checks if the word is already saved in the list or not to specify the icon. And there is an on-tap function which adds or removes the word-pair from the set and also changes the icon to green check if it is already saved from the pale add icon.
Dart
void _pushadd() => Navigator.of(context)        .push(MaterialPageRoute(builder: (BuildContext context) {      final Iterable<ListTile> tiles = _addWordPairs.map((WordPair pair) {        return ListTile(            title: Text(pair.asPascalCase, style: TextStyle(fontSize: 16.0)));      });       final List<Widget> divided =          ListTile.divideTiles(context: context, tiles: tiles).toList();                 // saved word-pair page      return Scaffold(          appBar: AppBar(title: Text('Saved Word-Pairs')),          body: ListView(children: divided));    }));//MaterialPageRoute
 The above code snippet generates the screen in which the word-pair along with the ListTile is listed takes from the set in which it was added earlier.
Complete Source Code:
Dart
import 'package:flutter/material.dart';import 'package:english_words/english_words.dart'; // importing dependenciesvoid main() => runApp(MyApp()); // inictating app buildclass MyApp extends StatelessWidget {  @override  Widget build(BuildContext context) {    return MaterialApp(      theme: ThemeData(primaryColor: Colors.green),      home: RandWords(),      debugShowCheckedModeBanner: false,    );  }} class RandWords extends StatefulWidget {  @override  RandWordsState createState() => RandWordsState();} class RandWordsState extends State<RandWords> {  final _randomWordPairs = <WordPair>[];  final _addWordPairs = Set<WordPair>();   Widget _buildList() {    return ListView.builder(      padding: const EdgeInsets.all(16.0),      itemBuilder: (context, item) {        if (item.isEven) return Divider();         final index = item ~/ 2;         if (index >= _randomWordPairs.length) {          _randomWordPairs.addAll(generateWordPairs().take(10));        }         return _buildRow(_randomWordPairs[index]);      },    );  }   Widget _buildRow(WordPair pair) {    final alreadyadd = _addWordPairs.contains(pair);         // word-pair tile    return ListTile(        title: Text(pair.asPascalCase, style: TextStyle(fontSize: 18.0)),        trailing: Icon(alreadyadd ? Icons.check : Icons.add,            color: alreadyadd ? Colors.green : null),        onTap: () {          setState(() {            if (alreadyadd) {              _addWordPairs.remove(pair);            } else {              _addWordPairs.add(pair);            }          });        });  }   void _pushadd() => Navigator.of(context)          .push(MaterialPageRoute(builder: (BuildContext context) {        final Iterable<ListTile> tiles = _addWordPairs.map((WordPair pair) {          return ListTile(              title: Text(pair.asPascalCase, style: TextStyle(fontSize: 16.0)));        });         final List<Widget> divided =            ListTile.divideTiles(context: context, tiles: tiles).toList();                     // saved word-pair page        return Scaffold(            appBar: AppBar(title: Text('Saved Word-Pairs')),            body: ListView(children: divided));      }));   // home page  Widget build(BuildContext context) => Scaffold(      appBar: AppBar(        title: Text('GeeksforGeeks Word-Pair Generator'),        actions: <Widget>[          IconButton(icon: Icon(Icons.menu_book), onPressed: _pushadd)        ],      ),      body: _buildList());}
Output: 
gulshankumarar231
simranarora5sos
arorakashish0911
android
Flutter
Picked
Dart
Flutter
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Flutter - DropDownButton Widget
Flutter - Custom Bottom Navigation Bar
Flutter - Checkbox Widget
ListView Class in Flutter
Flutter - Flexible Widget
Flutter - DropDownButton Widget
Flutter - Custom Bottom Navigation Bar
Flutter Tutorial
Flutter - Checkbox Widget
Flutter - Flexible Widget