preface

Hello, everyone, it’s been a long time since I dug gold, I’m back again!! Bring you to today or a login page, but today the login page of dark and bright, like the login page white and long, night mode believes everyone not unfamiliar, iOS and android’s new version also will support pattern in the night, so this article with all of you have know simple rough night in the Flutter model how to achieve ~

The effect

Implementation approach

In fact, there is nothing magical about night mode, it is just the replacement of resources, the simplest we can set a variable isDark to determine whether it is night mode, if it is night mode, load night resources, if it is not day resources. But we need to start thinking a little bit

  1. How do we set a global variable
  2. How do we notify components to update to replace resources

Dang dang dang dang ~InheritedWidgetTo help you

Let’s first think about how to tell components to update and replace resources. First of all, we can think about how to pass data between two components

class Test {
  var name;
  Test(this.name);
}

class A extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(child: B(test: Test('It's hard to fly')));
  }
}

class B extends StatelessWidget {
  final Test test;
  const B({Key key, this.test}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    returnText(test.name); }}Copy the code

Easy, but ah, but this is a very simple way to pass in data that is only good for very simple logical interactions on a page, if you think about it if we have seven or eight or more components on a page, it’s like, Logical hell! The Flutter development team is obviously aware of this, so we’re going to have to use the InheritedWidget now!

InheritedWidgetWhat kind of duck can you make?

In short, inheritedWidgets allow for efficient propagation (and sharing) of information down the widget tree. InheritedWidget is a special Widget that is placed in the Widget tree as a parent of another child tree. All widgets of the subtree must be able to interact with the data exposed by the InheritedWidget.

For example, with the 🌰 child, we have A TodoList application with A page A list page and A page B add page

B
A
InheritedWidget

InheritedWidgetHow to use

class _TodoInherited extends InheritedWidget {
  _TodoInherited({Key key, @required Widget child, this.data})
      : super(key: key, child: child);

  final TodoState data;

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return true; }}Copy the code

Very simply, all of the InheritedWidget’s children can interact with the data exposed by the InheritedWidget because it is a tree node. Since most of our data needs to be dynamically changed when using InheritedWidget, we are bound to use StatefulWidget, So the complete code should be as follows:

class Todo {
  String title;

  Todo(this.title);

}

class _TodoInherited extends InheritedWidget {
  _TodoInherited({Key key, @required Widget child, this.data})
      : super(key: key, child: child);

  final TodoState data;

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return true;
  }
}

class TodoWidget extends StatefulWidget {
  final Widget child;

  TodoWidget({Key key, this.child}) : super(key: key);

  static TodoState instanceOf(BuildContext context) {
    _TodoInherited inherited = (context
        .inheritFromWidgetOfExactType(_TodoInherited) as _TodoInherited);
    return inherited.data;
  }

  @override
  State<StatefulWidget> createState() => TodoState();
}

class TodoState extends State<TodoWidget> {
  List<Todo> _todoList = [];

  List<Todo> get todoList => _todoList;

  void addTodo(String title) {
    setState(() {
      _todoList.add(Todo(title));
    });
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    returnnew _TodoInherited(child: widget.child, data: this); }}Copy the code
  • TodoAs our data model is stored inTodoStateIn the
  • _TodoInheritedKept inTodoStateGet reference, convenient call to getTodo
  • _TodoInheritedIn theupdateShouldNotifyThe method is used to determine whether to update and rebuild
  • TodoWidgetIn theinstanceOfMethod is used to getTodoStateConvenient access to the data
  • instanceOfIn thecontext.inheritFromWidgetOfExactTypeIs used to register bindingsInheritedWidget

So our complete business code should look like this:

void main() {
  runApp(TodoWidget(child: MyApp()));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'TodoList', debugShowCheckedModeBanner: false, home: HomePage());
  }
}

class HomePage extends StatelessWidget {

  Widget buildEmpty() {
    return. ; } Widget buildList(List<Todo> list) {return. ; } @override Widget build(BuildContext context) { List<Todo> list = TodoWidget.instanceOf(context).todoList;return list.isEmpty ? buildEmpty() : buildList(list);
  }

}

class AddTaskPage extends StatelessWidget {

  final controller = TextEditingController();

  void closeTask(BuildContext context) {
    Navigator.pop(context);
  }

  Widget getTextField() {
    return TextField(controller: controller);
  }

  @override
  Widget build(BuildContext context) {
    returnonTap: () { TodoWidget.instanceOf(context).addTodo(controller.text); closeTask(context); }}}Copy the code

Having said so much, night mode!!

I believe you understand the above content for the implementation of night mode should also have bamboo in mind ~ HERE I introduce an implementation method

class _CustomTheme extends InheritedWidget {
  final CustomThemeState data;

  _CustomTheme({this.data, Key key, @required Widget child})
      : super(key: key, child: child);

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return true;
  }
}

class CustomTheme extends StatefulWidget {
  final Widget child;
  final MyThemeKeys initialThemeKey;

  const CustomTheme({Key key, this.initialThemeKey, this.child})
      : super(key: key);

  @override
  CustomThemeState createState() => CustomThemeState();

  static ThemeModel of(BuildContext context) {
    _CustomTheme inherited =
        (context.inheritFromWidgetOfExactType(_CustomTheme) as _CustomTheme);
    return inherited.data.theme;
  }

  static CustomThemeState instanceOf(BuildContext context) {
    _CustomTheme inherited =
        (context.inheritFromWidgetOfExactType(_CustomTheme) as _CustomTheme);
    return inherited.data;
  }
}

class CustomThemeState extends State<CustomTheme> {
  ThemeModel _model;

  ThemeModel get theme => _model;

  @override
  void initState() {
    _model = MyThemes.getThemeFromKey(widget.initialThemeKey);
    super.initState();
  }

  void changeTheme(MyThemeKeys themeKey) {
    print(themeKey);
    setState(() {
      _model = MyThemes.getThemeFromKey(themeKey);
    });
  }

  @override
  Widget build(BuildContext context) {
    return _CustomTheme(data: this, child: widget.child);
  }
}

enum MyThemeKeys { LIGHT, DARK }

class MyThemes {
  static final ThemeModel lightTheme = ThemeModel(
      imageUrl: 'assets/images/banner.png', backgroundColor: Color(0xFFffFFFF), titleColor: Color(0xff3C4859), borderColor: color.black. WithOpacity (0.3), isDark:false);

  static final ThemeModel darkTheme = ThemeModel(
      imageUrl: 'assets/images/banner_dark.png'BackgroundColor: Color(0xff2B1C71), titleColor: Color(0xffffffff), borderColor: color.white. WithOpacity (0.3), isDark:true);

  static ThemeModel getThemeFromKey(MyThemeKeys themeKey) {
    switch (themeKey) {
      case MyThemeKeys.LIGHT:
        return lightTheme;
      case MyThemeKeys.DARK:
        return darkTheme;
      default:
        return lightTheme;
    }
  }
}

void main() {
  runApp(CustomTheme(initialThemeKey: MyThemeKeys.LIGHT, child: MyApp()));
}

class MyApp extends StatelessWidget {
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        debugShowCheckedModeBanner: false, home: HomePage()); }}Copy the code

Then we can get fonts or colors directly from ThemeModel, but there are some special situations that need special treatment, Flutter can also be handled very gracefully and conveniently, such as:

Widget build(BuildContext context) { String image = CustomTheme.of(context).imageUrl; bool isDark = CustomTheme.of(context).isDark; Widget child = isDark ? Padding(...) : Padding(...) ;return child;
  }
Copy the code

Ok, so far our night mode has been implemented

Afterword.

This is still a very simple content sharing, but InheritedWidget is a very useful component that we all want to use! The landing page series continues, please stay tuned ~ 7 short of the Nuggets community’s 50 likes!!

thanks

Description of the core concepts of Flutter: Widget, State, Context, and InheritedWidget

Dynamic theming with Flutter – Flutter Community – Medium