pub.dev is Dart and Flutter’s official package repository. pub.dev has more than 20,000 open source packages. These packages suit your custom needs that the Flutter framework in itself may not suffice. At times, a package you need might not have the app features or UI customizations you want. In this article, you will learn how to customize Flutter packages. We will walk through how to access the source code of a given package, understand how it works, and customize it to suit your needs. To demonstrate the customization step, we will use an example package, FlutterToast.
How to Use FlutterToast
FlutterToast is a package that pops toast as feedback to the user. When using the package, you provide the FToast
class with the toast widget through the .showToast
method. Besides the child toast widget, the .showToast
method takes other arguments to customize the toast. These arguments include gravity, positionedToastBuilder, and toastDuration, which permits you to customize the toast as you want.
FlutterToast animates the toasts automatically. As of the current version of FlutterToast: v8, flutterToast animates your toast widgets with a FadeTransition. The toast subtly appears, then disappears after the set duration. The .showToast
method also takes a fadeDuration that specifies how long in milliseconds the fade transition should take place.
Let’s see this in action. Have Flutter installed and working. If you don’t have it and you want to follow along, install and set up Flutter from here. Create a new Flutter project by running the following command and add the FlutterToast package with the following commands:
flutter create exampletoast cd exampletoast flutter pub add fluttertoast
Open the newly created exampletoast
Flutter project in your Flutter enabled IDE. Delete the contents of the lib/main.dart
file and paste the following. Run the project in a Flutter device and interact with the buttons to see the toasts in action.
EMBED: https://gist.github.com/obumnwabude/368c3ebd8212e818fb28bb1bb2c67063
import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp(home: ExampleToast()); } } class ExampleToast extends StatefulWidget { const ExampleToast({Key? key}) : super(key: key); @override State<ExampleToast> createState() => ExampleToastState(); } class ExampleToastState extends State<ExampleToast> { late final FToast fToast; final Widget toast = Container( padding: const EdgeInsets.fromLTRB(16, 8, 16, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: Colors.greenAccent, ), child: const Text('Successful!'), ); @override void initState() { super.initState(); fToast = FToast(); fToast.init(context); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Example Toast')), body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ ElevatedButton( child: const Text('Show Toast'), onPressed: () => fToast.showToast(child: toast), ), const SizedBox(height: 32), ElevatedButton( child: const Text('Show Customized Toast'), onPressed: () => fToast.showToast( child: toast, fadeDuration: 3000, gravity: ToastGravity.TOP, toastDuration: Duration(seconds: 3), ), ), ], ), ), ); } }
Let’s say you are building an app and your product designers use slide transitions all through. You have used FlutterToast as the UI specifies. However, on test running, they are not okay with the fade transition. Your organization now requires you to use slide transition for the toast. You now have to customize the FlutterToast package.
To do this, you will have to access the source code of the FlutterToast package, understand how it works, and customize it to suit your needs (in this case use a SlideTransition instead of Fade).
How to Access the Source Code of Flutter Packages
Packages published on pub.dev are open source. They are public and available to use by anyone. Each package has its own package page on pub.dev. For a given package, you find its page at pub.dev/packages/PACKAGE-NAME.
On a package’s page, under the metadata info, there are links to the homepage and or repository for that package. Most of the time, these links will take you to where the code is hosted on GitHub. The package is open source. So you are sure the source code is always available. Either on GitHub or on a different source control platform (like GitLab or BitBucket).
For example, FlutterToast’s pub.dev package page is at pub.dev/packages/fluttertoast.
Clicking on the homepage link under metadata info takes you to the GitHub repository for FlutterToast.
In general, we can classify the ways to access the source code from GitHub into two:
- Online: for example, GitHub Codespaces, gitpod.io, vscode.dev, etc.
- Locally: cloning the repository to your local machine. (recommended)
Cloning the repository locally is the preferred way to access a package’s source code. The main advantage you have with this method is flexibility. Editing online in a browser is flexible, but it is not as flexible as editing in your preferred IDE, locally. Once you’ve cloned the repository of your choice package, open it in your preferred editor.
To clone FlutterToast, run the following command in your Terminal:
git clone https://github.com/PonnamKarthik/FlutterToast
It will download the FlutterToast repository. Open it in your IDE of choice. We will use Visual Studio Code in this article.
How to Understand Package Codebases
Locate where to make changes in the Flutter package you want to customize.
Unlike Flutter apps, Dart and Flutter packages don’t have a main method. They don’t have some specific entry point like runApp (in the case of Flutter). However, similar to Flutter apps, Dart and Flutter packages have a lib folder. This folder contains all the dart files (libraries) available to your Flutter apps when you import the package.
So our focus will be on the lib
folder. The lib
folder must contain at least one Dart file. It could have more. It could also have subfolders, which in turn would further contain more Dart files. These Dart files contain the package’s classes and constants.
To understand a package’s codebase, locate the declarations of classes you use (when you import the package). These declarations could further depend on other privately declared literals in the Dart file(s). To locate these declarations, use your IDE’s search features or read through the code. As you read, the various parts of the package will become clear.
Let’s follow up with our FlutterToast example. Open the lib folder in your editor. You’ll see two Dart files: fluttertoast.dart
and afluttertoastweb.dart
. Open the fluttertoast.dart
file.
In the example usage, we made use of the FToast
class and its .showToast
method to show toasts. Locate or search for them in the fluttertoast.dart
file. We would focus on the logic within the .showToast
method.
How to Customize FlutterToast to Have SlideTransition
The first block of code after the parameters declaration of the method goes as follows:
if (context == null) throw ("Error: Context is null, Please call init(context) before showing toast."); Widget newChild = _ToastStateFul( child, toastDuration ?? Duration(seconds: 2), fadeDuration: fadeDuration);
The first part is an if block that checks if context is null. If it is null, it throws an error indicating that .init(context)
has to be called. This explains why we called it in initState
in our example.
The next line is the declaration of some newChild
widget based on the child we provided. However, this newChild
uses a local and private widget _ToastStateFul
. The other parts of this method configure the gravity and position of the toast.
The .showToast
method returns the newChild instead of the child we provided. This indicates that the parent wrapper _ToastStateFul
widget has an effect on the displayed toast. This _ToastStateFul
widget is declared lower in the file. Get to it and examine its build method.
The build method of _ToastStateFul
starts with:
return FadeTransition( opacity: _fadeAnimation as Animation<double>,
This is where the FadeTransition is applied to all toasts displayed by this package. As it is hardcoded into the package, we have no choice than to customize it ourselves.
Replacing the above with:
return SlideTransition( position: Tween(begin: Offset(_startPos, 0), end: Offset(_endPos, 0)) .animate(_fadeAnimation as Animation<double>),
Will successfully change the FadeTransition to a SlideTransition as we want. But the above line introduces two new variables: _startPos
and _endPos
. These variables are absent in the _ToastStateFul
widget. So we have to include and handle them properly.
Making the above changes inside the cloned repo of FlutterToast has no effect. It has no effect because we can’t deploy this new version to pub.dev – we are not the package’s author. But then we want to use this version of the package in our own codebase. How do we go about that?
How to Use a Customized Flutter Package in Your Codebase
Because packages are open source, you are free to tweak and use them as you want. To use the customized version of a Flutter package, get the part of the code you need into your own project. Use this copied version and edit it as you like.
Let’s use the example project, FlutterToast. Go back to the example project we created at the beginning of this article. Create a new file in the lib folder and name it fluttertoast.dart
. Paste the entire contents of the fluttertoast.dart
file in the cloned FlutterToast package to this newly created file in the example project.
Change the first statement of the build method of _ToastStateFul
from FadeTransition to SlideTransition as specified in the previous section.
Introduce the _startPos
and _endPos
variables in the beginning of the enclosing State class. Declare these variables as the first lines of code within the ToastStateFulState
class. Change the class’ beginning to:
/// State for [_ToastStateFul] class ToastStateFulState extends State<_ToastStateFul> with SingleTickerProviderStateMixin { double _startPos = -1; double _endPos = 0;
This gets the toast to slide in from the left of the screen (-1) to the toast’s proper position (0). However, with this configuration, the toast will slide out to the left and it is not what we want. We want the toast to slide out to the right. We would need to update the position values before sliding out the toast.
The authors of the library included a hideIt()
function to animate the toast while it is being removed. This is the ideal place to toggle the _startPos
and _endPos
values. Toggle their values with setState
inside the hideIt()
function. Replace the declaration of hideIt()
with the following.
/// Start the hidding animations for the toast hideIt() { setState(() { _startPos = 1; _endPos = 0; }); _animationController!.reverse(); _timer?.cancel(); }
To make toasts animate with slide animations, we need to use this modified version instead of the default package. Replace the package’s import in main.dart from:
import 'package:fluttertoast/fluttertoast.dart';
to simply:
import 'fluttertoast.dart';
To use the just created and modified version of FlutterToast with slide animation instead of fade. Now run the example project’s main.dart file again. If you interact with the buttons, the toasts now open with slide animations instead of with fade.
Conclusion
While building apps, you might need some specific features that packages don’t come with. It is okay to customize the package’s code to implement the feature you want.
In this article, we used FlutterToast to demonstrate how to customize a package to suit your needs. In summary, the steps are as follows:
- Obtain the source code of the Flutter package
- Understand the package’s source code
- Customize the package to suit your needs.
As you build, asides from customizing, you would also want your app to be performant. At times, your app might process heavy workloads that consume CPU resources. Such excessive computations could prevent Flutter from properly rendering your app’s UI. This will cause the app to be janky and non-responsive. You will need to use separate Dart threads or Isolates for the heavy computations. Learn multithreading in flutter from here.