从零构建可扩展 Flutter 应用:v1.0 → v2.0 全代码详解 -《已适配开源鸿蒙》

从零构建可扩展 Flutter 应用:v1.0 → v2.0 全代码详解 -《已适配开源鸿蒙》
在这里插入图片描述

v1.0 → v2.0 全代码详解

在这里插入图片描述

从零构建可扩展 Flutter 应用:v1.0 → v2.0 全代码详解

副标题:一个干净、模块化、支持 Riverpod 状态管理的生产级起点

在 Flutter 开发中,很多项目失败并非因为技术不行,而是初期架构缺失。本文将手把手带你从 最简可行应用(v1.0) 出发,逐步演进到 支持状态管理、主题切换、本地持久化的 v2.0 架构,并附上全部代码 + 逐行解释,助你打造一个真正面向未来的 Flutter 应用。


🧱 第一阶段:v1.0 —— 干净的基础骨架

✅ 目标

  • 最小可运行应用
  • 标准 Material Design 结构
  • 清晰目录划分
  • 无第三方依赖

📁 项目结构

lib/ ├── main.dart ├── app.dart ├── screens/home_screen.dart └── widgets/app_bar_title.dart 

1. lib/main.dart —— 应用入口

import'package:flutter/material.dart';import'app.dart';voidmain(){runApp(constMyApp());}
说明main() 是 Dart 程序入口。调用 runApp() 启动 Flutter 应用。不在此处写业务逻辑,仅负责启动根组件 MyApp,符合单一职责原则。

2. lib/app.dart —— App 根组件

import'package:flutter/material.dart';import'screens/home_screen.dart';classMyAppextendsStatelessWidget{constMyApp({super.key});static bool get isDarkMode =>false;// 预留:未来可动态控制@override Widget build(BuildContext context){returnMaterialApp( title:'My App', debugShowCheckedModeBanner:false, theme:ThemeData( useMaterial3:true, colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), brightness: Brightness.light,), darkTheme:ThemeData( useMaterial3:true, colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue, brightness: Brightness.dark), brightness: Brightness.dark,), themeMode: isDarkMode ? ThemeMode.dark : ThemeMode.light, home:constHomeScreen(),);}}
说明:使用 MaterialApp 提供 Material Design 支持。定义 themedarkTheme,为深色模式做准备。home 指向主页面,路由系统预留位置(后续可替换为 routesGoRouter)。isDarkMode 是静态变量,便于未来替换为状态管理驱动的动态值

3. lib/widgets/app_bar_title.dart —— 可复用组件

import'package:flutter/material.dart';classAppBarTitleextendsStatelessWidget{final String title;constAppBarTitle({super.key, required this.title});@override Widget build(BuildContext context){returnText( title, style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold,),);}}
说明:将 AppBar 标题封装为独立 widget,提高复用性。使用 Theme.of(context) 获取当前主题样式,自动适配深浅色。通过 required this.title 强制传参,避免空值错误。

4. lib/screens/home_screen.dart —— 主页面

import'package:flutter/material.dart';import'../widgets/app_bar_title.dart';classHomeScreenextendsStatefulWidget{constHomeScreen({super.key});@override State<HomeScreen>createState()=>_HomeScreenState();}class_HomeScreenStateextendsState<HomeScreen>{ int _counter =0;void_incrementCounter(){setState((){ _counter++;});}@override Widget build(BuildContext context){returnScaffold( appBar:AppBar( title:constAppBarTitle(title:'My App'), centerTitle:true,), body:Center( child:Column( mainAxisAlignment: MainAxisAlignment.center, children:<Widget>[constText('Welcome to My App v1.0!', style:TextStyle(fontSize:20, fontWeight: FontWeight.w500),),constSizedBox(height:24),Text('You have pushed the button this many times:'),Text('$_counter', style: Theme.of(context).textTheme.headlineMedium,),constSizedBox(height:32), ElevatedButton.icon( onPressed: _incrementCounter, icon:constIcon(Icons.add), label:constText('Increment'),),],),), floatingActionButton:FloatingActionButton( onPressed: _incrementCounter, tooltip:'Increment', child:constIcon(Icons.add),),);}}
说明:使用 StatefulWidget 管理局部计数器状态。_counter 是私有变量,通过 setState 触发 UI 更新。页面结构清晰:AppBar + Body + FloatingActionButton。局限性:状态无法跨页面共享,重启后丢失——这正是 v2.0 要解决的问题。

⚙️ 第二阶段:v2.0 —— 引入 Riverpod,实现可扩展架构

✅ 升级目标

  • 状态全局可访问
  • 支持深色/浅色/系统主题动态切换
  • 计数器数据持久化(重启不丢失)
  • 解耦 UI 与逻辑

📦 新增依赖(pubspec.yaml

dependencies:flutter:sdk: flutter flutter_riverpod: ^2.5.1 # 状态管理shared_preferences: ^2.2.2 # 本地存储
运行命令:

这里也可以直接进行保存

在这里插入图片描述

📁 v2.0 项目结构

lib/ ├── main.dart ├── app.dart ├── constants/app_colors.dart (可选) ├── providers/ │ ├── app_theme_provider.dart │ └── counter_provider.dart ├── screens/home_screen.dart ├── widgets/ │ ├── app_bar_title.dart │ └── theme_toggle_button.dart └── utils/logger.dart (预留) 
在这里插入图片描述

1. lib/main.dart —— 包裹 ProviderScope

import'package:flutter/material.dart';import'package:flutter_riverpod/flutter_riverpod.dart';import'app.dart';voidmain()async{ WidgetsFlutterBinding.ensureInitialized();// 必须在使用 shared_preferences 前调用runApp(constProviderScope(// Riverpod 的根容器 child:MyApp(),),);}
说明ProviderScope 是 Riverpod 的上下文提供者,必须包裹整个应用WidgetsFlutterBinding.ensureInitialized() 是使用平台通道(如 SharedPreferences)的必要步骤。

2. lib/providers/app_theme_provider.dart —— 主题状态管理

import'package:flutter/material.dart';import'package:flutter_riverpod/flutter_riverpod.dart';enum AppThemeMode { light, dark, system }// 用户选择的主题模式(可持久化)final appThemeModeProvider = StateProvider<AppThemeMode>((ref){return AppThemeMode.light;// TODO: 未来从 SharedPreferences 读取});// 计算实际生效的 ThemeModefinal effectiveThemeModeProvider = Provider<ThemeMode>((ref){final mode = ref.watch(appThemeModeProvider);switch(mode){case AppThemeMode.light:return ThemeMode.light;case AppThemeMode.dark:return ThemeMode.dark;case AppThemeMode.system:return ThemeMode.system;}});
说明StateProvider 用于简单状态(如枚举、bool、int)。effectiveThemeModeProvider 是一个派生状态,根据用户选择计算出 ThemeMode未来可轻松扩展:将初始值从 SharedPreferences 读取,实现“记住用户偏好”。

3. lib/providers/counter_provider.dart —— 持久化计数器

import'package:flutter_riverpod/flutter_riverpod.dart';import'package:shared_preferences/shared_preferences.dart';final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref){returnCounterNotifier();});classCounterNotifierextendsStateNotifier<int>{CounterNotifier():super(0){_loadFromPrefs();// 初始化时加载} Future<void>_loadFromPrefs()async{final prefs =await SharedPreferences.getInstance();final saved = prefs.getInt('counter')??0; state = saved;// 触发 UI 更新} Future<void>increment()async{ state++;// 自动通知监听者final prefs =await SharedPreferences.getInstance();await prefs.setInt('counter', state);// 保存} Future<void>reset()async{ state =0;final prefs =await SharedPreferences.getInstance();await prefs.setInt('counter', state);}}
说明StateNotifierProvider 适合管理复杂状态逻辑(如异步、验证、副作用)。state = newValue 会自动通知所有监听者重建 UI。数据在 increment()reset() 中自动持久化到设备存储。

4. lib/widgets/theme_toggle_button.dart —— 主题切换按钮

import'package:flutter/material.dart';import'package:flutter_riverpod/flutter_riverpod.dart';import'../providers/app_theme_provider.dart';classThemeToggleButtonextendsConsumerWidget{constThemeToggleButton({super.key});@override Widget build(BuildContext context, WidgetRef ref){final currentMode = ref.watch(appThemeModeProvider);return PopupMenuButton<AppThemeMode>( onSelected:(mode)=> ref.read(appThemeModeProvider.notifier).state = mode, itemBuilder:(context)=>[PopupMenuItem( value: AppThemeMode.light, child:Row(children:[Icon(Icons.light_mode, color: Colors.yellow[700]),constSizedBox(width:8),constText('Light'),]),),PopupMenuItem( value: AppThemeMode.dark, child:Row(children:[Icon(Icons.dark_mode, color: Colors.blue[300]),constSizedBox(width:8),constText('Dark'),]),),PopupMenuItem( value: AppThemeMode.system, child:Row(children:[Icon(Icons.settings, color: Colors.grey),constSizedBox(width:8),constText('System'),]),),], child:IconButton( icon: currentMode == AppThemeMode.dark ?constIcon(Icons.dark_mode):constIcon(Icons.light_mode), tooltip:'Switch theme',),);}}
说明ConsumerWidget 允许使用 ref.watch 监听状态变化。ref.read(...) 用于写入状态(不监听变化,性能更好)。图标根据当前主题动态切换,提升用户体验。

5. lib/app.dart —— 使用 Riverpod 主题

import'package:flutter/material.dart';import'package:flutter_riverpod/flutter_riverpod.dart';import'screens/home_screen.dart';import'providers/app_theme_provider.dart';classMyAppextendsConsumerWidget{constMyApp({super.key});@override Widget build(BuildContext context, WidgetRef ref){final themeMode = ref.watch(effectiveThemeModeProvider);// 监听主题变化returnMaterialApp( title:'My App', debugShowCheckedModeBanner:false, theme:ThemeData(useMaterial3:true, colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue)), darkTheme:ThemeData( useMaterial3:true, colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue, brightness: Brightness.dark),), themeMode: themeMode, home:constHomeScreen(),);}}
关键点MyAppStatelessWidget 变为 ConsumerWidget,以便监听 effectiveThemeModeProvider。当用户切换主题时,MaterialApp 自动重建,应用新主题。

6. lib/screens/home_screen.dart —— 使用 Riverpod 状态

import'package:flutter/material.dart';import'package:flutter_riverpod/flutter_riverpod.dart';import'../providers/counter_provider.dart';import'../widgets/app_bar_title.dart';import'../widgets/theme_toggle_button.dart';classHomeScreenextendsConsumerWidget{constHomeScreen({super.key});@override Widget build(BuildContext context, WidgetRef ref){final count = ref.watch(counterProvider);// 自动监听计数器变化returnScaffold( appBar:AppBar( title:constAppBarTitle(title:'My App v2.0'), centerTitle:true, actions:[constThemeToggleButton()],// 主题切换按钮), body:Center( child:Column( mainAxisAlignment: MainAxisAlignment.center, children:[constText('Welcome to My App!', style:TextStyle(fontSize:20, fontWeight: FontWeight.w500)),constSizedBox(height:24),constText('Counter (persisted):'),Text('$count', style: Theme.of(context).textTheme.headlineMedium),constSizedBox(height:32), ElevatedButton.icon( onPressed:()=> ref.read(counterProvider.notifier).increment(), icon:constIcon(Icons.add), label:constText('Increment'),), TextButton.icon( onPressed:()=> ref.read(counterProvider.notifier).reset(), icon:constIcon(Icons.refresh), label:constText('Reset'),),],),), floatingActionButton:FloatingActionButton( onPressed:()=> ref.read(counterProvider.notifier).increment(), tooltip:'Increment', child:constIcon(Icons.add),),);}}
关键改进:不再使用 StatefulWidget,UI 与状态完全解耦。ref.watch(counterProvider) 自动更新计数显示。按钮通过 ref.read(...).increment() 触发状态变更。重启应用后,计数器值依然保留!

测试

在这里插入图片描述


在这里插入图片描述

🔮 未来扩展路径

这个 v2.0 架构已为以下能力预留“插槽”:

功能扩展方式
多页面导航添加 go_router,在 app.dart 中配置
API 请求创建 api_provider.dart,使用 FutureProvider
用户登录新增 auth_provider.dart,管理 Token 和用户信息
国际化启用 flutter_localizations,添加 ARB 文件
日志监控utils/logger.dart 中封装 Sentry/Firebase

例如,添加天气 API 只需:

// providers/weather_provider.dartfinal weatherProvider = FutureProvider<Weather>((ref)async{final res =await http.get(Uri.parse('https://api.weather.com/...'));return Weather.fromJson(json.decode(res.body));});

然后在 UI 中优雅处理加载/错误/数据状态:

ref.watch(weatherProvider).when( data:(weather)=>Text('${weather.temp}°C'), loading:()=>CircularProgressIndicator(), error:(err, _)=>Text('Failed to load'),);

✅ 总结

  • v1.0 是一个规范、可交付的起点,避免“脏快糙”陷阱。
  • v2.0 通过 Riverpod + 模块化设计,实现了状态集中管理、UI 逻辑解耦、数据持久化。
  • 所有代码均可直接复制使用,并具备良好的扩展性。
好的架构不是一开始就复杂的,而是从第一天起就为“变化”做好准备。

📥 附录:完整文件清单

你可以按以下顺序创建文件:

  1. lib/main.dart
  2. lib/app.dart
  3. lib/constants/app_colors.dart(可选)
  4. lib/providers/app_theme_provider.dart
  5. lib/providers/counter_provider.dart
  6. lib/screens/home_screen.dart
  7. lib/widgets/app_bar_title.dart
  8. lib/widgets/theme_toggle_button.dart
  9. lib/utils/logger.dart(可留空)

然后更新 pubspec.yaml 并运行 flutter pub get


Read more

【开源神器】只需3分钟,教你打造属于自己的微信自动化发送工具!

【开源神器】只需3分钟,教你打造属于自己的微信自动化发送工具!

🚀彻底解放双手!微信消息自动化发送脚本工具实战教程 🌈 个人主页:创客白泽 - ZEEKLOG博客 🔥 系列专栏:🐍《Python开源项目实战》 💡 热爱不止于代码,热情源自每一个灵感闪现的夜晚。愿以开源之火,点亮前行之路。 👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更多人哦 📌 概述 在当今数字化办公场景中,自动化工具已成为提升工作效率的利器。本文将深入剖析一个基于Python的微信自动化工具开发全过程,该工具集成了即时消息发送、定时任务管理和微信进程控制三大核心功能模块。 技术栈亮点: * PyQt5构建美观的GUI界面 * uiautomation实现Windows UI自动化 * psutil进行进程管理 * 多线程处理保持UI响应 * 完整的异常处理机制 🛠️ 功能全景 1. 核心功能模块 模块名称功能描述即时消息发送支持文本+文件混合发送,智能识别联系人定时任务管理精确到秒的定时发送,支持循环任务配置微信进程控制启动/激活/退出微信的一键操作 2. 特色功能 * 智能窗口激活:自动置顶微信窗口并居中显示

By Ne0inhk

睡前定方向,醒来收初稿:全自动跑实验改论文的工作流开源了

与其在实验室通宵,不如让 Claude 替你卷。 如果你还在熬夜手搓代码、调参跑实验,那这个刚刚开源的科研工作流绝对会让你眼前一亮。 它就是 ARIS(Auto-Research-In-Sleep),一款真正帮你实现“睡后科研”的全自动神器。 这个项目的核心理念很直接,让 Claude Code 在你睡觉时做科研。 睡前丢给 AI 一篇论文初稿,醒来就能发现,站不住脚的 claim 已被剔除,20 多组 GPU 实验默默跑完,整篇论文的叙事框架焕然一新,分数也从 5.0 稳步提升到了可投稿的 7.5 分——而且全流程零人工干预。 作为一套专为机器学习科研定制的 Claude Code Skills,ARIS 既吸收了 FARS 的经验,也呼应了 Karpathy 提出的 autoresearch

By Ne0inhk

GitHub 镜像站点

国内访问 GitHub 有时会遇到速度慢或不稳定的情况,这时 GitHub 镜像站点就能帮上忙。它们通过代理或缓存机制,让你更顺畅地浏览仓库、下载资源甚至克隆代码。 下面表格汇总了一些常见的镜像站及其主要用途 镜像站点名称访问地址主要特点适用场景 bgithub.xyz https://bgithub.xyz/直接替换域名访问,操作简单日常浏览仓库、克隆代码 kkgithub.com https://kkgithub.com/直接替换域名,支持代码查看和 Issue日常浏览仓库、查看 Issues gitclone.com https://gitclone.com/提供在线工具生成克隆命令,适合命令行操作需要快速获取仓库克隆命令 kgithub.com https://kgithub.com/支持代码查看、Issue 和评论,但不支持注册和文件上传阅读代码、参与讨论(无需上传文件) ghproxy.net https:

By Ne0inhk
ClawPanel — 开源 OpenClaw 智能管理面板,20+ 通道接入 / 多模型配置 / Docker 一键部署

ClawPanel — 开源 OpenClaw 智能管理面板,20+ 通道接入 / 多模型配置 / Docker 一键部署

🐾 一个比官方控制台更强大的 OpenClaw 可视化管理工具,支持 QQ、微信、Telegram、Discord 等 20+ 通道统一管理,多 AI 模型提供商配置,技能中心,版本管理,环境检测,Docker 一键部署。 📌 项目简介 ClawPanel 是一个基于 React + TypeScript + Express 的 OpenClaw 智能管理面板,旨在为 OpenClaw 用户提供一个比官方控制台更强大、更直观的可视化管理工具。 项目前身是 openclaw-im-manager(一个简单的 QQ 机器人管理后台),经过 4 个大版本迭代,现已进化为功能完整的 OpenClaw 全能管理面板。 GitHub 地址:https://github.com/zhaoxinyi02/ClawPanel

By Ne0inhk