Easy way to get user Feedback in Flutter

Flutter Jul 29, 2023

Gathering user feedback is very important for improving your Flutter application. In this post, we will talk about the feedback package, which makes it easy for users to send feedback. With this package, users can submit a screenshot along with a description. They can even draw on the screenshot to make their point clear. Let us see how this package can help you improve your application based on valuable user input.

Installing

To get started, we need to install the feedback package into our project. The installation process is simple. Just execute the following command: flutter pub add feedback.

Once the command is executed, make sure to check your pubspec.yaml file for the added dependencies. You should see the feedback package included in the dependencies section, like this:

dependencies:
  feedback: ^2.6.0
  flutter:
    sdk: flutter

Capturing user feedback

Now that the package is successfully installed, let us begin with a basic implementation. The feedback package provides a BetterFeedBack widget that requires a child widget. The widget should be the root of the widget tree. Specifically, it should be above any Navigator widgets, including the navigator provided by the MaterialApp widget, let us go over the example:

import 'package:feedback/feedback.dart';
import 'package:flutter/material.dart';

void main() => runApp(BetterFeedback(child: MyApp()));

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    void feedback() {
      BetterFeedback.of(context).show((UserFeedback feedback) {
        showDialog(
          context: context,
          builder: (BuildContext context) => SimpleDialog(
            title: Column(
              children: [
                Text(feedback.text, style: TextStyle(fontSize: 24)),
                SizedBox(height: 10),
                Image.memory(feedback.screenshot, width: 250, height: 500),
              ],
            ),
          ),
        );
      });
    }

    return MaterialApp(
      title: 'Feedback demo',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Feedback demo'),
        ),
        body: Center(
          child: SingleChildScrollView(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  child: Text('Give Feedback'),
                  onPressed: feedback,
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

In this code snippet, we start by wrapping the MyApp widget with the BetterFeedback widget. The BetterFeedback widget is responsible for the feedback-capturing process.

Inside the MyApp widget, we created a feedback function. In this function we call the BetterFeedback.of(context).show which opens the feedback panel. When the feedback is submitted the function retrieves the feedback and displays it in a dialog using the showDialog function.  In the ShowDialog we return a SimpleDialog which contains a Column widget that displays a Text widget with the feedback text and a Image.memory widget that displays the screenshot of the feedback. Be aware that the screenshot is returned as Uint8List.

The MyApp widget itself returns a Scaffold widget that displays an ElavatedButton. When the button is clicked it will invoke the feedback function opening the feedback panel, see the GIF below:

flutter_feedback_panel_submitting_drawing_and_text

Handling multiple pages

Now that we have covered the basic usage, let us take it a step further and see how the package works with applications that have multiple pages. Take a look at the following code snippet:

main.dart

import 'package:codeonwards_demo/custom_drawer.dart';
import 'package:feedback/feedback.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BetterFeedback(
      child: MaterialApp(
        title: 'Feedback demo',
        routes: {
          '/profile-page': (context) => ProfilePage(),
        },
        home: HomePage(),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Feedback demo'),
      ),
      drawer: CustomDrawer(),
      body: SizedBox(),
    );
  }
}

class ProfilePage extends StatelessWidget {
  const ProfilePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Profile page'),
      ),
      body: SizedBox(),
    );
  }
}

In this code snippet, the MyApp widget builds a MaterialApp that is wrapped by the BetterFeedback widget. It defines a title and one named route ("/profile-page") for the ProfilePage. The HomePage widget is returned on the home property, which means that the HomePage will have the default route name ("/").

The HomePage widget displays a Scaffold widget with an AppBar widget containing the title "Feedback demo". It also includes a CustomDrawer to enable navigation to different pages within the application.

The ProfilePage widget also displays a Scaffold widget with an AppBar widget containing the title "Profile page." The body of both the HomePage and ProfilePage widgets are returning an empty SizedBox widget.

custom_drawer.dart

import 'package:feedback/feedback.dart';
import 'package:flutter/material.dart';

class CustomDrawer extends StatelessWidget {
  const CustomDrawer({super.key});

  @override
  Widget build(BuildContext context) {
    return Drawer(
      child: ListView(
        children: [
          ListTile(
            leading: Icon(Icons.home),
            title: Text('Home'),
            onTap: () => Navigator.pushNamed(context, '/'),
          ),
          ListTile(
            leading: Icon(Icons.person),
            title: Text('Profile page'),
            onTap: () => Navigator.pushNamed(context, '/profile-page'),
          ),
          ListTile(
            leading: Icon(Icons.feedback),
            title: Text('Feedback'),
            onTap: () => BetterFeedback.of(context).show(
              (UserFeedback feedback) => showDialog(
                context: context,
                builder: (BuildContext context) => SimpleDialog(
                  title: Column(
                    children: [
                      Text(
                        feedback.text,
                        style: TextStyle(fontSize: 24),
                      ),
                      SizedBox(height: 10),
                      Image.memory(
                        feedback.screenshot,
                        width: 250,
                        height: 500,
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

In this code snippet, we created CustomDrawer widget, which is used for navigation in the application. The CustomDrawer widget also contains our feedback functionality. The build method of the CustomDrawer widget returns a Drawer widget. The Drawer is a material design component that provides a panel from the left side of the screen to display navigation options.

Within the Drawer, a ListView widget is used to organize the different navigation options vertically. The ListView has three children, each represented by a ListTile widget.

Home ListTile

  • leadingIcon: A home icon (Icons.home).
  • title: "Home".
  • onTap : When the user taps this item, it triggers the onTap function, which uses Navigator.pushNamed(context, '/') to navigate to the default route ("/"). This means the user will be taken to the home page of the application when they tap the "Home" item in the drawer.

Profile ListTile

  • leadingIcon: A person icon (Icons.person).
  • title: "Profile page".
  • onTap: When the user taps this item, it triggers the onTap function, which uses Navigator.pushNamed(context, '/profile-page') to navigate to the named route for the profile page "/profile-page". This means the user will be taken to the profile page of the application when they tap the "Profile page" item in the drawer.

Feedback ListTile

  • leadingIcon: A feedback icon (Icons.feedback).
  • title: "Feedback".
  • onTap: When the user taps this item, it triggers the onTap function, which uses the same functionality as the previous implementation to display the feedback panel, retrieve its values, and present them in a dialog.

When we run our application we will now have a multi-page application. As you can see within the feedback panel we are free to navigate through the application and can provide feedback on every page:

flutter_feedback_panel_navigating_to_different_screen

Conclusion

In this post, we explored the feedback package for Flutter, which offers a simple and user-friendly solution for gathering valuable user feedback. With this package, users can easily provide feedback by submitting screenshots with descriptions and even drawing on the images. We learned how to install the package and implement its basic usage, creating an easy feedback-capturing system within the application. Additionally, we discovered how to handle multiple pages using the feedback package, enabling navigation and feedback submission from various sections of the application.

Tags