从零构建可扩展 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

Vscode中配置Claude code的git bash链接问题

解决VS Code中Claude Code的Git Bash链接问题 问题描述 在VS Code中使用Claude Code时出现错误提示: Error: Claude Code on Windows requires git-bash (https://git-scm.com/downloads/win). 确定git已经安装成果,且按照官方建议设置环境变量CLAUDE_CODE_GIT_BASH_PATH仍无效。 解决方案 删除特定环境变量 在Windows环境变量的用户变量部分,检查并删除CLAUDE_CODE_GIT_BASH_PATH变量(如果存在)。 将Git CMD添加到PATH 编辑用户变量中的Path,添加Git的cmd文件夹路径: * 用户级安装路径:%USERPROFILE%\AppData\Local\Programs\Git\cmd * 全局安装路径:C:\Program Files\

By Ne0inhk
【已开源】【嵌入式 Linux 音视频+ AI 实战项目】瑞芯微 Rockchip 系列 RK3588-基于深度学习的人脸门禁+ IPC 智能安防监控系统

【已开源】【嵌入式 Linux 音视频+ AI 实战项目】瑞芯微 Rockchip 系列 RK3588-基于深度学习的人脸门禁+ IPC 智能安防监控系统

前言 本文主要介绍我最近开发的一个个人实战项目,“基于深度学习的人脸门禁+ IPC 智能安防监控系统”,全程满帧流畅运行。这个项目我目前全网搜了一圈,还没发现有相关类型的开源项目。这个项目只要稍微改进下,就可以变成市面上目前流行的三款产品,人脸识别门禁系统、IPC 安防和 NVR。在最下面会有视频演示。 本项目适用于瑞芯微 Rockchip 系列的板端,开源链接在文章最下面。 功能 人脸门禁系统 * 人靠近自动亮屏,人走自动息屏 * 支持人脸识别 * 支持录入人脸,并进行人脸配对(极速配对 < 0.2S) IPC 智能安防监控系统 * 支持通过 onvif 实时查看摄像头画面 * 支持实时目标检测(支持高达80种物体检测) * 支持录像 * 支持检测到人时自动录像 * 支持检测到人时自动报警 用到的硬件 * 野火鲁班猫4 RK3588S2 * IMX415 800W 4k 摄像头 * RTL8822CE Wifi+BT

By Ne0inhk
Flutter for OpenHarmony: Flutter 三方库 github 在鸿蒙应用中深度集成 GitHub API 构建高效的开发者协作工具(出海与工具链开发)

Flutter for OpenHarmony: Flutter 三方库 github 在鸿蒙应用中深度集成 GitHub API 构建高效的开发者协作工具(出海与工具链开发)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在进行 OpenHarmony 为核心的开发者工具、代码托管助手或出海类社交应用开发时,与 GitHub 的数据打交道是必修课。我们需要实现: 1. 鸿蒙端展示用户的 GitHub 仓库列表和 Stars。 2. 自动化管理 Issues,或是监控 Repo 的 PR 动态。 3. 利用 GitHub 账号直接登录鸿蒙端的开发者社区。 github 软件包是 Flutter 生态中功能最全、维护最稳健的 GitHub REST API 封装库。它提供了 100% 强类型的接口定义,让你的鸿蒙应用能以最轻量化的方式接入全球最大的开源社区数据。 一、API 通讯与对象模型 github 库将繁杂的 JSON

By Ne0inhk
OpenClaw(龙虾)开源AI智能体科普解析:核心原理、功能特性与本地部署教程

OpenClaw(龙虾)开源AI智能体科普解析:核心原理、功能特性与本地部署教程

近期开源AI领域,OpenClaw(俗称“龙虾”)凭借其本地优先、可定制的特性,受到开发者社区的广泛关注,其项目保活程度与社区活跃度可通过GitHub数据直观体现:目前该项目已获得222k stars、1.2k watching、42.3k forks,各项数据均处于开源AI智能体领域前列,足以证明其社区认可度与持续更新能力。作为一款开源AI智能体工具,它在办公自动化、系统辅助等场景具有实用价值,适合开发者了解和落地实践。 OpenClaw是一款开源的个人AI助手编排平台,采用TypeScript开发,目前在GitHub上拥有较高的关注度,其核心价值在于将大模型的推理能力与本地系统操作相结合,打破了传统AI助手“仅能交互、无法执行”的局限。本文将从技术科普角度,围绕OpenClaw的核心定义、功能特性、技术细节及本地部署步骤展开,帮助开发者全面了解这款工具的原理与使用方法。 对于ZEEKLOG的开发者群体而言,了解OpenClaw的技术架构与应用场景,既能拓展AI智能体的认知边界,也能将其应用于日常开发、办公场景,提升工作效率。 本文将从「核心定义、功能特性、技术细节、本地部署」

By Ne0inhk