There are so many great libraries on pub.dev these days it can be hard to sift through them all. The flutter team maintains their own list of favorites, and we thought it might be worthwhile to put together a list of our own favorite libs!
Some of these we published ourselves, and others are third party packages that we think are just excellent additions to the core. In no particular order (other than alphabetical :p) here is a collection of some of our favorite flutters libs:
AnimateDo
AnimateDo
is our goto package for declarative animations. Inspired by Animate.css it offers a wide range of animations, from FadeIn
to ZoomOut
and everything in between.
return FadeInLeft( child: FlutterLogo(), delay: Duration(seconds: 1), );
Collection
The collection
package adds a List
extensionfirstWhereOrNull
which solves one of the biggest pain points with nnbd
; the difficulty when calling list.firstWhere(..., orElse: () => null)
:
List<int> list = []; // nnbd compiler cries about this int? item = list.firstWhere((i) => i == 0, orElse: () => null); // this works :) int? item = list.firstWhereOrNull((i) => i == 0);
While one extension method probably does not justify an entire package, collection
adds tons of useful extensions and sorting algorithms for working with lists including shuffle
, binarySearch
, whereNot
, forEachIndexed
and many more.
Equatable
When it comes to comparing complex objects for equality, equatable
is an absolute life saver. Now, instead of overridding ==
operator and hashCode
you can simply extend Equatable
and return a list of props
:
class Person extends Equatable { const Person(this.name); final String name; @override List<Object> get props => [name]; }
ExtraAlignments
Why should Center
get all the love? If you are sick of typing Align( alignment: Alignment
this package is for you. It adds additional alignments to help make your layouts more readable (and give your fingers a break):
TopLeft
TopCenter
TopRight
CenterLeft
CenterRight
BottomLeft
BottomCenter
BottomRight
Flextras
flextras
provides several specialized types of Flex widgets to help ease common use cases:
`SeparatedColumn
/Row
– When you want to have some consistent gap between elements in a Row
or Column
:
return SeparatedColumn( // 10px gap between each element separatorBuilder: () => SizedBox(height: 10), children: [ ... ] )
ExpandedScrollingColumn
/Row
– When you have some some expanding content, that should begin scrolling if it runs out of room. This is a common problem because you normally can not have Expanded
, Flexible
or Spacer
widgets inside of a SingleChildScrollView
without flutter throwing infinite bounds layout errors.
// This will expand vertically to fill the parent, but begin scrolling when it has to ExpandedScrollingColumn( children: [ Expanded(child: Container(height: 400)), ] )
Gap
Forget SizedBox(height: 10)
for padding, now you can just do Gap(10)
:
return Column( children: [ const Gap(20), // Adds an empty space of 20 pixels. Container(color: Colors.red, height: 20), ], );
This will work properly in either a Column
or Row
! This might seem minor, but it eliminates an entire class of bugs from your UI code (setting height, when you meant width, or vice-versa), and is also more readable.
GetIt + GetItMixin
GetIt
it allows you to provide testable singletons that can be accessed from anywhere in your app. GetItMixin
lets you bind your UI to those singletons. Together they are one of the most elegant state management solutions around.
// Declare a controller/manager/bloc class SettingsManager { ValueNotifier<bool> darkMode = ValueNotifier(false); } ... // Register that controller as a singleton GetIt.registerSingleton(SettingsManager()); SettingsManager get settings => GetIt.I.get<SettingsManager>(); ... // Access the manager to trigger actions, or bind to data class SettingsDrawer extends StatelessWidget with GetItMixin { @override Widget build(BuildContext context) { // Bind to `darkMode` property, we will rebuild when it changes bool darkMode = watchX((SettingsManager m) => m.darkMode); return Column(children: [ Checkbox( value: darkMode, // Change the darkMode value when pressed onChanged: (value) => settings.darkMode.value = value, ), ]); } }
GetIt
is quite similar to packages like provider
or riverpod
but it eschews the need for a context
or ref
to manage scope. Instead it uses a simple concept of pushing and popping scopes. This approach not only make usage straightforward and clean, but testing and mocking also become trivial.
GoRouter
Our favorite “Nav 2” routing package at the moment is GoRouter
. It offers a clean declarative API for declaring the page structure of your app, has good documentation and an exhaustive test suite.
class App extends StatelessWidget { final _router = GoRouter( routes: [ GoRoute( path: '/', builder: (context, state) => const Page1Screen(), ), GoRoute( path: '/page2', builder: (context, state) => const Page2Screen(), ), ], ); @override Widget build(BuildContext context) => MaterialApp.router( routeInformationParser: _router.routeInformationParser, routerDelegate: _router.routerDelegate, ); }
ModalBottomSheet
While the built-in showBottomSheet
and showModalBottomSheet
are a good start they are somewhat limited in their ability to be customized and have a number of other limitations. ModalBottomSheet
addresses these issues by adding:
- Support for inside scrollview + dragging down to close (
showModalBottomSheet
won’t work correctly with scrollviews) - Support for
WillPopScope
to prevent closing the dialog. - Support for scroll to top when tapping status bar (iOS only)
- Cupertino modal bottom sheets
- Custom modal bottom sheet
showMaterialModalBottomSheet( context: context, builder: (context) => Container(), )
For an interactive demo of the various configurations check out the online demo here: https://jamesblasco.github.io/modal_bottom_sheet/#/
Reactives
If you haven’t noticed, flutter has a state problem. In short: everytime you use a stateful object that must tie into widget life-cycle, you have to repeat the same verbose, error-prone code, or use a builder which reduces legibility.
A classic example is the management of the AnimationController
lifecycle.
class _MyWidgetState extends State<MyWidget> with TickerProviderStateMixin { final AnimationController fadeAnim; @override void initState() { super.initState(); fadeAnim = AnimationController(vsync: this); } @override void dispose() { fadeAnim.dispose(); super.dispose(); } // do stuff with `fadeAnim` }
Not only is this 13 lines of pointless boilerplate and noise, it’s error-prone. If you forget to dispose
, you have a bug. We could potentially use a TweenAnimationBuilder
to avoid the potential bugs, but it is quite verbose and hurts readability.
With reactives
you can write this as a single line:
class _MyWidgetState extends State<MyWidget> with ReactiveHostMixin { late final fadeAnim = ReactiveAnimationController(this); // do stuff with `fadeAnim.ctrl` }
Not only is this less code, but it’s more robust. It’s impossible to forget to cleanup, as it is handled by the reactive controller.
Other helpful reactives include ReactiveTextEditingController
, ReactiveFuture
, ReactiveFocusNode
, ReactivePageController
, ReactiveTabController
, ReactiveScrollController
and ReactiveStream
. It’s also very easy to make your own if needed.
SimpleRichText
Speaking of problems in flutter… crafting rich text is currently one of the primary pain points when building UI. Luckily SimpleRichText
provides a quick and easy syntax to build rich text strings:
SimpleRichText(r'*_/this is all three*_/ (*{color:red}bold*, _{color:green}underlined_, and /{color:brown}italicized/). _{push:home;color:blue}clickable hyperlink to home screen_')
Some of the other possibilities include:
'this is /italic/' 'this is *bold*' '*_/this is all three*_/ (*bold*, _underlined_, and /italicized/)' 'you can quote characters to NOT format a word \*bold\*' 'this is _underline_' 'go to _{/myroute}home_ page' 'this is ~important~(red).' 'this is _*bold and underlined*_.'
StatsFl
A simple FPS monitor for flutter, StatsFl
is very useful for monitoring performance in release builds and during distributed testing, to identify jank.
runApp(StatsFl(child: MyApp()));
What did we miss?
That’s it for us! We hope you found a couple of new packages that will ease your life in Flutter. Please let us know below in the comments if you have any other favorites of your own that you think we missed.
Need help building something cool in Flutter? We’d love to chat.