Flutter深色模式适配的实现

一、简介

Flutter的深色模式以及跟随系统设置比较简单,我感觉需要注意的是开发过程中尽量使用Theme中的颜色与样式,开发过程中遇到的比较大的坑就是provider的一些问题,可能是因为我用的版本新一些,网上找了很多文章,总会遇到一些问题。本文的深色模式适配是通过修改themeMode来实现的,供诸位有缘人参考。

二、环境介绍

1. Flutter: 2.0.3
2. Dart: 2.12.0
3. provider: 5.0.0
状态管理,用于运行时切换主题
4. shared_preferences: 2.0.5
数据持久化,用于保存当前选中的主题,以便下次启动时读取使用用户选择的主题

environment:
 sdk: ">=2.12.0 <3.0.0"

dependencies:
 flutter:
 sdk: flutter

 # 忽略了一些依赖...

 # shared_preferences https://pub.flutter-io.cn/packages/shared_preferences
 shared_preferences: ^2.0.5
 # 全局状态管理 https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md
 provider: ^5.0.0

三、主题

1. ThemeData

factory ThemeData({
 Brightness brightness, // 应用主题亮度,可选(dark、light)
 VisualDensity visualDensity, // 视觉密度
 MaterialColor primarySwatch, // 主要样式,设置primaryColor后该背景色会被覆盖
 Color primaryColor, // 主要部分背景颜色(导航和tabBar等)
 Brightness primaryColorBrightness, // primaryColor的亮度
 Color primaryColorLight, // primaryColor的浅色版
 Color primaryColorDark, // primaryColor的深色版
 Color accentColor, // 前景色(文本,按钮等)
 Brightness accentColorBrightness, // accentColor的亮度
 Color canvasColor, // MaterialType.canvas 的默认颜色
 Color shadowColor, // 阴影颜色
 Color scaffoldBackgroundColor, // Scaffold的背景颜色。典型Material应用程序或应用程序内页面的背景颜色
 Color bottomAppBarColor, // BottomAppBar的默认颜色
 Color cardColor, // Card的颜色
 Color dividerColor, // Divider和PopupMenuDivider的颜色,也用于ListTile之间、DataTable的行之间等。
 Color focusColor, // 突出颜色
 Color hoverColor, // hoverColor
 Color highlightColor, // 高亮颜色,选中在泼墨动画期间使用的突出显示颜色,或用于指示菜单中的项。
 Color splashColor, // 墨水飞溅的颜色。InkWell
 InteractiveInkFeatureFactory splashFactory, // 定义由InkWell和InkResponse反应产生的墨溅的外观。
 Color selectedRowColor, // 用于突出显示选定行的颜色。
 Color unselectedWidgetColor, // 用于处于非活动(但已启用)状态的小部件的颜色。例如,未选中的复选框。通常与accentColor形成对比。也看到disabledColor。
 Color disabledColor, // 禁用状态下部件的颜色,无论其当前状态如何。例如,一个禁用的复选框(可以选中或未选中)。
 Color buttonColor, // RaisedButton按钮中使用的Material 的默认填充颜色。
 ButtonThemeData buttonTheme, // 定义按钮部件的默认配置,
 ToggleButtonsThemeData toggleButtonsTheme, // 切换按钮的主题
 Color secondaryHeaderColor, // 选定行时PaginatedDataTable标题的颜色。
 Color textSelectionColor, // 文本框中文本选择的颜色,如TextField
 Color cursorColor, // 文本框中光标的颜色,如TextField
 Color textSelectionHandleColor, // 调整当前选定的文本部分的句柄的颜色。
 Color backgroundColor, // 与主色形成对比的颜色,例如用作进度条的剩余部分。
 Color dialogBackgroundColor, // Dialog元素的背景颜色
 Color indicatorColor, // 选项卡中选定的选项卡指示器的颜色。
 Color hintColor, // 用于提示文本或占位符文本的颜色,例如在TextField中。
 Color errorColor, // 用于输入验证错误的颜色,例如在TextField中
 Color toggleableActiveColor, // 用于突出显示Switch、Radio和Checkbox等可切换小部件的活动状态的颜色。
 String fontFamily, // 文本字体
 TextTheme textTheme, // 文本的颜色与卡片和画布的颜色形成对比。
 TextTheme primaryTextTheme, // 与primaryColor形成对比的文本主题
 TextTheme accentTextTheme, // 与accentColor形成对比的文本主题。
 InputDecorationTheme inputDecorationTheme, // 基于这个主题的 InputDecorator、TextField和TextFormField的默认InputDecoration值。
 TabBarTheme tabBarTheme, // 用于自定义选项卡栏指示器的大小、形状和颜色的主题。
 TooltipThemeData tooltipTheme, // tooltip主题
 CardTheme cardTheme, // Card的颜色和样式
 AppBarTheme appBarTheme, // appBar主题
 ColorScheme colorScheme, // 拥有13种颜色,可用于配置大多数组件的颜色。
 NavigationRailThemeData navigationRailTheme, // 导航边栏主题
 // ...
 })

2. main.dart or MaterialApp

theme为默认主题,darkTheme为深色主题,themeMode为当前使用哪个主题,可选值system、light、dark,只有在th``eme与darkTheme都设置的时候才会生效,我们的theme与darkTheme都直接使用ThemeData对象,给他指定了brightness,而不是使用这样感觉可以方便修改样式,当然也可以抽出来封装一下,我这没有去处理。

MaterialApp(
 theme: ThemeData(
 brightness: Brightness.light,
 // scaffoldBackgroundColor: Color(0xFFF5F5F9),
 ),
 darkTheme: ThemeData(
 brightness: Brightness.dark,
 // scaffoldBackgroundColor: Color(0xFFF5F5F9),
 ),
 themeMode: context.watch<ThemeModel>().theme
);

四、全局配置

全局配置是在MaterialApp加载之前进行一写初始化操作,参考了《Flutter实战》电子书,Flutter当中SharedPreferences是异步初始化,还有Dio网络请求的缓存也需要提前初始化,我们这里SharedPreferences加载完之后在进行之后的操作,SpUtils中的SharedPreferences使用的Global全局配置中的静态属性。

1. Global

class Global {
 static late SharedPreferences prefs;

 static ThemeMode theme = ThemeMode.light;

 // 是否为release版
 static bool get isRelease => bool.fromEnvironment("dart.vm.product");

 //初始化全局信息,会在APP启动时执行
 static Future init() async {
 WidgetsFlutterBinding.ensureInitialized();
 prefs = await SharedPreferences.getInstance();

 // 当前本地存储的主题
 String themeValue = await SpUtils.instance.getStorage(SpConstants.skin);
 theme = themeStringToThemeMode(themeValue);

 //初始化网络请求相关配置
 HttpManager();
 }
}

2. main.dart

// Global加载完成后掉用runApp
Global.init().then((e) => runApp());

3. themeStringToThemeMode()

字符串转ThemeMode

ThemeMode themeStringToThemeMode(String themeValue){
 ThemeMode theme = ThemeMode.light;
 switch (themeValue) {
 case "light":
  theme = ThemeMode.light;
  break;
 case "dark":
  theme = ThemeMode.dark;
  break;
 case "system":
  theme = ThemeMode.system;
  break;
 }
 return theme;
}

五、使用状态管理(provider)切换主题

> 此处大坑,处处劝退,感谢Flutter provider劝退经历这篇文章

1. 构建主题Model

class ThemeModel extends ChangeNotifier {

 // 获取当前主题,如果为设置主题,则默认使用浅色模式
 ThemeMode get theme => Global.theme;

 // 主题改变后,通知其依赖项,新主题会立即生效
 set theme(ThemeMode themeMode) {
 if (themeMode != theme) {
  Global.theme = themeMode;
  notifyListeners();
 }
 }
}

2. main.dart(监听值变化)

此处为main.dart文件的完整代码,下面有关provider的一些使用方式可能与网上很多文章不一样的,但是这都是官网文档的最新推荐使用方式。读取当前provider中存储的主题context.watch<ThemeModel>().theme

void main() {
 //顶部状态栏透明
 SystemChrome.setSystemUIOverlayStyle(
  SystemUiOverlayStyle(statusBarColor: Colors.transparent));
 Global.init().then((e) => runApp(
  MultiProvider(
   providers: [ListenableProvider<ThemeModel>(create: (_) => ThemeModel())],
   builder: (context, child) {
    return WanAndroid();
   }),
  ));
}

class WanAndroid extends StatelessWidget {
 // This widget is the root of your application.
 @override
 Widget build(BuildContext context) {
 return MaterialApp(
  initialRoute: '/',
  theme: ThemeData(
  brightness: Brightness.light,
  // scaffoldBackgroundColor: Color(0xFFF5F5F9),
  ),
  darkTheme: ThemeData(
  brightness: Brightness.dark,
  // scaffoldBackgroundColor: Color(0xFFF5F5F9),
  ),
  themeMode: context.watch<ThemeModel>().theme,
  routes: {
  '/': (context) => SplashPage(),
  '/index': (context) => IndexPage(),
  '/login': (context) => LoginPage(),
  '/setting': (context) => SettingPage(),
  },
  title: '玩Android-Flutter版',
 );
 }
}

3. 切换主题

修改provider中保存的值即可。

// themeStringToThemeMode方法代码在上面有写
context.read<ThemeModel>().theme = themeStringToThemeMode(value);

六、源码

- 源码:github.com/sdwfqin/flutter_wanandroid

到此这篇关于Flutter深色模式适配的实现的文章就介绍到这了,更多相关Flutter深色模式适配内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解Flutter的路由导航

    Flutter 的路由导航 路由管理或导航管理:从一个页面平滑地过渡到另一个页面,我们需要有一个统一的机制来管理页面之间的跳转.在原生的Android 开发,是通过startActivity或startActivityForResult 来完成页面的跳转的,在Flutter 中如何实现呢? 在 Flutter 中,页面之间的跳转是通过 Route 和 Navigator 来管理的: Route 是页面的抽象,主要负责创建对应的界面,接收参数,响应 Navigator 打开和关闭: 而 Navig

  • 详解Flutter混排瀑布流解决方案

    背景 流式布局,这是一种当前无论是前端,还是Native都比较流行的一种页面布局.特别是对于商品这样的Feeds流,无论是淘宝,京东,美团,还是闲鱼.都基本上以多列瀑布流进行呈现,容器列数固定,然后每个卡片高度不一,形成参差不齐的多栏布局. 对于Native来说,无论是iOS还是Android,CollectionView和RecyclerView都能满足我们的绝大部分场景了.不过目前闲鱼很多业务场景都是在Flutter上进行实现的,当时Flutter官方只提供了ListView和GridVie

  • 浅谈Flutter解析JSON三种方式

    Dart实体类格式 class CategoryMo { String name; int count; CategoryMo({this.name, this.count}); //将map转成mo CategoryMo.fromJson(Map<String, dynamic> json) { name = json['name']; count = json['count']; } //将mo转成map,可缺省 Map<String, dynamic> toJson() {

  • 详解Flutter 调用 Android Native 的方法

    Flutter 调用 Android Native 的方法,是通过MethodChannel的方式来实现的: 在Android端: 创建一个Class,实现FlutterPlugin和MethodCallHandler接口 重写onAttachedToEngine(),onDetachedFromEngine(),onMethodCall() onAttachedToEngine()中,根据自定义的CHANNEL_NAME创建MethodChannel, onDetachedFromEngine

  • Android原生项目集成Flutter解决方案

    了解一下如何在 Android 原生项目中集成 Flutter 生成配置 在原生项目根目录执行命令 flutter create -t module --org {package_name} {module_name} // 此处 module_name 的命令遵循 Android 子 module 的命名即可.不能有中划线. // 比如, flutter create -t module --org com.engineer.mini.flutter flutter_sub // 此处 mod

  • 新版Flutter集成到已有Android项目的实现

    FlutterSDK升级后,我们发现不能按照原来的方式集成到已有Android项目中了,因为没有了Flutter这个类,通过阅读源码我找到了新的使用方式. 一.创建Flutter Module 在已有Android工程中集成flutter,可以使用AndroidStudio的new Flutter Module实现,方便快捷. 找到FlutterModule,一连串的next操作即可 创建完成后我们的项目中会多一个叫做flutter的module 并且在项目的setting.gradle文件中会

  • Flutter实现图片滤镜效果

    本着学习的态度,研究了一下flutter里面的ColorFilter,字面意思翻译颜色过滤器,学习就是要举一反三,拓展思考就把这个功能做了一个简单的图片滤镜效果.(ps:图片有点没控制住,有点长) 效果如下: ColorFilter 介绍 两种使用方式 const ColorFilter.mode(Color color, BlendMode blendMode) const ColorFilter.matrix(List<double> matrix) 实现效果主要通过第一种方式, 首先创建

  • Flutter网络请求库DIO的基本使用

    1. 导入dio包 目前dio库的最新版本是3.0.1,同使用其他三方库一样,Flutter中使用dio库同样需要配置pubspec.yaml文件. dependencies: flutter: sdk: flutter dio: ^3.0.10 2. 导入并创建实例 dio包引入成功之后就可以创建dio实例了,一个实例可以发起多个请求,APP中如果只有一个数据源的情况下就可以考虑将dio实例创建成单例模式,这样可以节省系统资源,减少不必要的开销. //htpp.dart import 'pac

  • 详解Flutter点击空白隐藏键盘的全局做法

    开发原生页面的时候,在处理键盘事件上,通常的需求是,点击输入框外屏幕,要隐藏键盘,同样的,这样的需求也需要在 Flutter 上实现, Android 上的实现方式是在基类 Activity 里实现事件分发,判断触摸位置是否在输入框内. /** * 获取点击事件 */ @CallSuper @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.MotionEv

  • Flutter深色模式适配的实现

    一.简介 Flutter的深色模式以及跟随系统设置比较简单,我感觉需要注意的是开发过程中尽量使用Theme中的颜色与样式,开发过程中遇到的比较大的坑就是provider的一些问题,可能是因为我用的版本新一些,网上找了很多文章,总会遇到一些问题.本文的深色模式适配是通过修改themeMode来实现的,供诸位有缘人参考. 二.环境介绍 1. Flutter: 2.0.3 2. Dart: 2.12.0 3. provider: 5.0.0 状态管理,用于运行时切换主题 4. shared_prefe

  • iOS13适配深色模式(Dark Mode)的实现

    好像大概也许是一年前, Mac OS系统发布了深色模式外观, 看着挺刺激, 时至今日用着也还挺爽的 终于, 随着iPhone11等新手机的发售, iOS 13系统也正式发布了, 伴随着手机版的深色模式也出现在了大众视野 我们这些iOS程序猿也有事情做了, 原有项目适配iOS13系统, 适配Dark Mode深色模式 虽然现在并没有要求强制适配Dark Mode, 但是DarK适配却也迫在眉睫 Apps on iOS 13 are expected to support dark mode Use

  • Android显示富文本+夜间深色模式

    目录 前言 方案一: 存在问题: 方法二: 富文本内容: 演示效果: 总结 前言 在工作中有遇到这样的需求,需要把hmtl的富文本内容,进行深色模式适配.原先的富文本内容是在直接在webview上进行展示. 解决思路:替换html中的内容色值. 方案一: 直接使用replace进行字符串替换,当时是去判断.标签,例如下代码 newText.replace("<p>", "<p><span style=\"color: rgb(51, 5

  • Flutter适配深色模式的方法(DarkMode)

    1.瞎叨叨 也不知道写点什么,本来想写写Flutter的集成测试.因为前一阵子给flutter_deer写了一套,不过感觉也没啥内容,写不了几句话就放弃了.(其实本篇内容也不多...) 那就写写最近在做的事情.没错,就是文章标题提到的适配深色模式(DarkMode),也可以说是实现夜间模式的功能.相信许多iOS的同学最近都比较关注,毕竟iOS 13上个月推送更新了. 说适配的原因是因为在iOS 13 和 Android 10系统上它都属于新特性.适配的目的是为了达到应用的主题随着系统主题模式的切

  • AndroidQ(10)黑暗模式适配的实现

    前言:作为一个Android程序员,每年最期待就是Google的发布会啦!!这不,今年的AndroidQ如期而至.这里简单介绍一下Android的新特性: AndroidQ全局暗黑模式 隐私权限的更新 AndroidQ新版的手势导航(其实就是仿IOS) 系统日程UI的优化(还有其他系统UI上的优化) Google组件(jetpack)的推荐 每年的Google大会一结束就是程序员忙碌工作的开端,各种适配,各种新功能- 一堆事情下来,搞的焦头烂额. 但是今年的发布会之后,仔细一看Q的更新清单,其实

  • flutter 屏幕尺寸适配和字体大小适配的实现

    前言: 现在的手机品牌和型号越来越多,导致我们平时写布局的时候会在个不同的移动设备上显示的效果不同, 比如我们的设计稿一个View的大小是300px,如果直接写300px,可能在当前设备显示正常,但到了其他设备可能就会偏小或者偏大,这就需要我们对屏幕进行适配. 安卓原生的话有自己的适配规则,可以根据不同的尺寸建立不同的文件夹,系统会根据当前的设备尺寸取对应的大小的布局.而flutter本身并没有适配规则,而原生的又比较繁琐,这就需要我们自己去对屏幕进行适配. 点击直达github地址 如果有帮助

  • Android界面一键变灰开发深色适配模式编程示例

    目录 深色主题工具类 background_color公用背景色 values/colors.xml 的代码 values-night/colors.xml 的代码 Android 界面一键变灰 java kotlin 深色主题工具类 package com.example.kotlindemo.utils import android.content.Context import android.content.res.Configuration import androidx.appcomp

  • Flutter 图片开发核心技能快速掌握教程

    目录 正文 使用网络图片 把网络图片缓存到磁盘 使用 assets 图片 适配浅色与深色模式 在不同的设备使用不同分辨率的图片 关于设备 dpr 不完全匹配的处理 忽略 dpr 信息 使用相册图片 使用相机拍摄的图片 使用内存图片 图片用做装饰 图片预加载 centerSlice centerSlice 只能放大,不能缩小. 全局缓存 ImageCache 的设置 图片类之间的关系 ImageProvider obtainKey(ImageConfiguration) 方法 resolve(Im

  • Android 10 适配攻略小结

    相比较去年写的Android 9适配,这次Android 10的内容有点多.没想到写了我整整两天,吐血中... 准备工作 老规矩,首先将我们项目中的 targetSdkVersion 改为 29. 1.Scoped Storage(分区存储) 说明 在Android 10之前的版本上,我们在做文件的操作时都会申请存储空间的读写权限.但是这些权限完全被滥用,造成的问题就是手机的存储空间中充斥着大量不明作用的文件,并且应用卸载后它也没有删除掉.为了解决这个问题,Android 10 中引入了 Sco

  • Flutter 如何设置App的主色调与字体

    Flutter 的主题色和字体可以在MaterialApp 中设置,即在 main.dart 的入口返回的 MaterialApp 组件统一设置全局的主色调和字体.如下面的代码所示: class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'App 框架', theme: ThemeData( primaryColor: Colo

随机推荐