Flutter关于一个登陆页之夜间模式

前言

Hello,你们好,又是很久没有水掘金了,我又回来啦!!今天给你们带来得仍是一个登陆页,不过今天这个登陆页又黑又亮,就像这个登陆页又白又长,夜间模式相信你们都不陌生,AndridiOS得全新版本也立刻要支持夜间模式了,因此本篇文章带你们简单粗略得认识下在Flutter中夜间模式怎么实现~bash

效果

01

实现思路

提及夜间模式其实并无什么神奇之处,只不过是资源得替换罢了,最简单得咱们能够设置一个变量isDark用来判断是不是夜间模式,若是是则加载夜间资源若是不是则加载日间资源。 不过具体实现起来咱们就须要小小得开动下脑筋了less

  1. 咱们如何设置一个全局变量
  2. 咱们如何通知组件更新去替换资源

铛铛铛~InheritedWidget来帮你

咱们首先来思考第一个问题如何通知组件去更新替换资源,首先咱们能够想一想简单得两个组件间如何传递数据,咱们能够经过构造方法等比较粗暴得传入好比~ide

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

class A extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(child: B(test: Test('苏武难飞')));
  }
}

class B extends StatelessWidget {
  final Test test;
  const B({Key key, this.test}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Text(test.name);
  }
}
复制代码

easy,可是啊可是,这种简单得传入只适合很是简单得页面逻辑交互时使用,想一想一下咱们某个页面有七八个甚至于更多组件时,这样传数据简直就是,逻辑地狱呀Flutter得开发团队显然也是意识到这点了因此咱们本篇得主角InheritedWidget就该出场了!post

InheritedWidget能够作啥鸭?

简而言之,InheritedWidget 容许在 widget 树中有效地向下传播(和共享)信息。 InheritedWidget 是一个特殊的 Widget,它将做为另外一个子树的父节点放置在 Widget 树中。该子树的全部 widget 都必须可以与该 InheritedWidget 暴露的数据进行交互。字体

举个🌰子,咱们有一个TodoList应用,有一个页面A列表页,有一个B添加页 ui

02
为了让 B添加页添加以后在 A列表页能够直接看到,咱们使用 InheritedWidget

InheritedWidget怎样使用

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;
  }
}
复制代码

很是简单,InheritedWidget得特殊性在于它做为一个树节点其全部得子节点均可以可以与该InheritedWidget暴露得数据进行交互。又因为要使用InheritedWidget得时候咱们的数据大部分都是须要动态改变得,So,咱们势必要使用StatefulWidget,那么咱们得完整代码就应该以下:this

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
    return new _TodoInherited(child: widget.child, data: this);
  }
}
复制代码
  • Todo做为咱们得数据模型保存在TodoState
  • _TodoInherited中保存TodoState得引用,方便调用获取Todo
  • _TodoInherited中的updateShouldNotify方法是用来判断是否更新重建
  • TodoWidget中的instanceOf方法是用来获取TodoState方便获取其中的数据
  • instanceOf中的context.inheritFromWidgetOfExactType是用来注册绑定InheritedWidget

因此咱们的完整业务代码应该以下:spa

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) {
    return onTap: () {
      TodoWidget.instanceOf(context).addTodo(controller.text);
      closeTask(context);
    }
  }
}
复制代码

03

说了这么多,夜间模式呢!!

相信你们理解了上面的内容对于夜间模式的实现应该也是胸中有竹子啦~我这边介绍一个实现方法debug

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: Colors.black.withOpacity(0.3),
      isDark: false);

  static final ThemeModel darkTheme = ThemeModel(
      imageUrl: 'assets/images/banner_dark.png',
      backgroundColor: Color(0xff2B1C71),
      titleColor: Color(0xffffffff),
      borderColor: Colors.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());
  }
}

复制代码

而后咱们字体或者颜色等能够直接从ThemeModel中获取,可是有一些比较特殊的状况可能须要特殊处理时,Flutter也能很是优雅方便的处理,好比:3d

Widget build(BuildContext context) {
    String image = CustomTheme.of(context).imageUrl;
    bool isDark = CustomTheme.of(context).isDark;
    Widget child = isDark
        ? Padding(...)
        : Padding(...);
    return child;
  }
复制代码

Ok,至此咱们的夜间模式也算是实现了~

04

后记

本篇依然是一个很是简单的内容分享,可是InheritedWidget是一个很是实用好用的组件,但愿你们都熟练掌握!!登陆页系列还在继续,请你们敬请期待吧~ 距离掘金社区50个赞还差7个!!

鸣谢

[译] Flutter 核心概念详解: Widget、State、Context 及 InheritedWidget

Dynamic theming with Flutter - Flutter Community - Medium

相关文章
相关标签/搜索