Your Second Flutter App

Nov 30 2021 · Dart 2.13, Flutter 2.2.3, Visual Studio Code

Part 5: Meet Inherited Widgets

33. Create an Inherited Widget

Episode complete

Play next episode

Next
About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 32. Learn Flutter State Management Next episode: 34. Lift State Up

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Transcript: 33. Create an Inherited Widget

An InheritedWidget is useful for lifting state up because it can be accessed by any widget in the widget tree.

InheritedWidgets are used to send information down the widget tree. To successfully use an InheritedWidget,

you can wrap it inside a StatefulWidget. The combination acts as a central storage from which widgets lower in the widget can access app state.

For the RWCourses app, the filterValue state for the CoursesPage and FilterPage will be lifted up in the widget tree, so that both pages can access the same state. In this episode, you’ll create the StatefulWidget and InheritedWidget combo called FilterStateContainer. In the next episode, you’ll let state up.

To get started, open your project in progress or download the sample project for this episode. Now run your app. First, create a new top level state folder.

Next, create a new file called filter_state_container.dart. We’re going to do things a little differently. Normally, we’d just have Visual Studio Code create our StatefulWidget template. In this case, we’re going to write it manually. You’ll see why in just a moment.

First, create a StatefulWidget called FilterStateContainer making sure to import the material library.

import 'package:flutter/material.dart';

class FilterStateContainer extends StatefulWidget {

}

Next create a widget property and then add a constructor.

final Widget child;

const FilterStateContainer({Key? key, required this.child}) : super(key: key);

Next, we’re going to override createState to return a filter state object. We’re getting a compile error because we haven’t made this class yet.

  @override
  State<StatefulWidget> createState() => FilterState();

The filter state is our internal state. You’ll notice that it’s a public. This is so we can access it in other parts of the app.

Let’s add a class stub for now.

class FilterState extends State<FilterStateContainer> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

Next, use the of pattern as a static method on the container to return the FilterState contained in the StatefulWidget

class FilterStateContainer extends StatefulWidget {
  ...
  static FilterState of(BuildContext context) {
    return context
        .dependOnInheritedWidgetOfExactType<_FilterInheritedWidget>()!
        .state;
  }

We call dependOnInheritedWidgetOfExactType, which is typed by the _FilterInheritedWidget that we’ll create soon. Calling this method on the context means of method that we declared will return the closest inherited widget in the widget tree which returns a FilterState.

Now give FilterState a filterValue int property and a SharedPreferences property.

class FilterState extends State<FilterStateContainer> {
  int filterValue = Constants.allFilter;
  late SharedPreferences _prefs;
}

Make sure to import both Contents.dart and SharedPreferences.

import '../constants.dart';

Now create a loadValue method to read the filterValue from sharedPreferences, just like we did earlier on FilterPage and CoursesPage.

class FilterState extends State<FilterStateContainer> {
  ...
  @override
  void initState() {
    super.initState();

    _loadValue();
  }

  void _loadValue() {
    SharedPreferences.getInstance().then((value) {
      _prefs = value;
      setState(() {
        filterValue = _prefs.getInt(Constants.filterKey) as int;
      });
    });
  }

Create an updateFilterValue that will later be used to update the saved filter value on the FilterPage.

class FilterState extends State<FilterStateContainer> {
  ...
  void updateFilterValue(int value) {
    setState(() {
      _prefs.setInt(Constants.filterKey, value);
      filterValue = value;
    });
  }

Ok, now it’s finally time to create the InheritedWidget class, a private class called _FilterInheritedWidget

class _FilterInheritedWidget extends InheritedWidget {
}

The inherited widget has a FilterState property, and also needs a constructor that populates the state and a child for the inherited widget.

class _FilterInheritedWidget extends InheritedWidget {
  final FilterState state;

  const _FilterInheritedWidget({
    Key? key,
    required this.state,
    required Widget child,
  }) : super(key: key, child: child);

We also need to override updateShouldNotify on the inherited widget

class _FilterInheritedWidget extends InheritedWidget {
  ...
  @override
  bool updateShouldNotify(_FilterInheritedWidget oldWidget) => true;

By returning true, we’re saying that all the widgets that depend on this inherited widget should rebuild when the state of the inherited widget changes.

Next we provide the necessary build method on filter state, which returns a _FilterInheritedWidget.

class FilterState extends State<FilterStateContainer> {
  ...
  @override
  Widget build(BuildContext context) {
    return _FilterInheritedWidget(
      state: this,
      child: widget.child,
    );
  }

The state of the _FilterInheritedWidget is the current FilterState instance, and the child is the same child as that for the FilterStateContainer widget.

Ok that completes the FilterStateContainer and _FilterInheritedWidget. Next up, you’ll use them to lift up state in the app.