Open In App

Flutter – Mark as Favorite Feature

Improve
Improve
Improve
Like Article
Like
Save Article
Save
Share
Report issue
Report

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.

Making a Card on the Home Screen:

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 experiences
                    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.

Making the screen scrollable:

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.

Adding favorite 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 dependencies
void 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 experiences 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 experiences 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.

Getting Dependencies:

 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 dependencies
void main() => runApp(MyApp());
 
//initiating app build
class 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.
 

Creating StateFul widget:

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.

Favorite Feature:

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.

Generating a new screen:

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 dependencies
void main() => runApp(MyApp());
 
//initiating app build
class 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: 



Last Updated : 26 Sep, 2022
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads