Flutter: Our favorite pub.dev libraries

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_')
Screenshot

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.

shawn.blais

Shawn has worked as programmer and product designer for over 20 years, shipping several games on Steam and Playstation and dozens of apps on mobile. He has extensive programming experience with Dart, C#, ActionScript, SQL, PHP and Javascript and a deep proficiency with motion graphics and UX design. Shawn is currently the Technical Director for gskinner.

@tree_fortress

Leave a Reply

Your email address will not be published. Required fields are marked *