Flutter: Creating your own Inherited Widgets

While we generally use Provider or GetIt to pass things around in Flutter, there are times when you don’t want to have any dependencies on these libraries, and instead just want to define your own MyFoo.of(context) lookup. Often this is when you’re creating packages yourself.

There are quite a few deep dives tutorials into this around, but in this post we wanted to keep it extremely short and sweet.

First of all, there are 2 main types of InheritedWidgets you might create, Stateless (some data) or Stateful (some controller). Both are very easy, lets look at Stateless first!

Create a Stateless InheritedWidget

Lets say you have a widget, and you want to allow someone above you in the tree to pass you some simple data. Like maybe your widget relies on a special String that you want to inherit.

Step1) Create an inherited widget that takes the Data you want to inherit:

class MyPathProvider extends InheritedWidget {
  MyPathProvider ({Key? key, required Widget child, required this.path}) : super(key: key, child: child);
  
  final String path;
  Widget build(BuildContext context) => child;

}

Step2) Add .of() and updateShouldNotify() methods to your widget:

static MyPathProvider ? of(BuildContext context) =>
context.dependOnInheritedWidgetOfExactType<MyPathProvider>();

@override
// rebuild children if .path value changes
bool updateShouldNotify(covariant MyPathProvider oldWidget) => oldWidget.path != path;

Step3) Use the widget in your tree:

return MyUserProvider(path: "some/path", child: ...) 

You’re done! You can now try and look this up using MyUserProvider.of(context).user from any descendants of MyUserProvider.

Create a Stateful InheritedWidget

Step 1) Create a StatefulWidget that takes a child, and declares it’s state as public.

class MyFoo extends StatefulWidget {
  const MyFoo({Key? key, required this.child}) : super(key: key);
  final Widget child;
  @override
  MyFooState createState() => MyFooState();
}

class MyFooState extends State<MyFoo> {
  @override
  Widget build(BuildContext context) => widget.child;
}

Step 2) Create a matching InheritedWidget, with a field for your state.

class _MyInheritedFoo extends InheritedWidget {
  _MyInheritedFoo({Key? key, required Widget child, required this.state}) : super(key: key, child: child);
  final MyFooState state;
  @override
  // Always rebuild children if state changes
  bool updateShouldNotify(covariant InheritedWidget oldWidget) => true;
}

Step 3) Inside your state’s build method, wrap the .child in the InheritedWidget, and pass the state as an argument:

Widget build(BuildContext context) {
    return _MyInheritedFoo(child: widget.child, state: this);
  }

Step 4) Add a single method to your StatefulWidget:

static MyFooState of(BuildContext context) =>
    (context.dependOnInheritedWidgetOfExactType<_MyInheritedFoo>() as _MyInheritedFoo).state;

Step5) Use the widget in your tree return MyFoo(child: …)

You’re done! You can now look up this state from anywhere in the tree, with a simple: MyFooState state = MyFoo.of(context);

You can view the full working example on github. Happy coding 🙂

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

One Comment

  1. Should

    final User user;

    be

    final String path;

    ?

Leave a Reply

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