如何在Flutter笔记应用中实现Provider,尤其是ChangeNotifierProvider?
实现步骤
你之前将ChangeNotifier混入页面State的用法不符合Provider的设计规范,ChangeNotifier需要作为独立的状态管理类存在,和页面UI代码解耦,以下是完整改造方案:
第一步:新增独立的Note状态管理类
新建note_provider.dart文件,代码如下:
import 'package:flutter/foundation.dart'; import 'package:note_taking_app/db/db_operations.dart'; import 'package:note_taking_app/db/model_notes.dart'; class NoteProvider extends ChangeNotifier { List<Note> _noteList = []; final dbHelper = DatabaseHelper.instance; // 对外暴露只读的笔记列表,避免外部直接修改状态 List<Note> get noteList => _noteList; // 初始化加载所有笔记 Future<void> loadNotes() async { await dbHelper.initDatabase(); _noteList = await dbHelper.getNotesFromDB(); notifyListeners(); } // 新增笔记 Future<void> addNote(Note note) async { await dbHelper.insert(note); // 新增后重新加载列表更新状态 await loadNotes(); } // 按ID查询单条笔记 Future<Note?> getNoteById(int id) async { final rows = await dbHelper.queryRows(id.toString()); if (rows.isNotEmpty) { return Note.fromMap(rows.first); } return null; } }
第二步:配置依赖并在顶层注册Provider
首先在pubspec.yaml中添加provider依赖:
dependencies: flutter: sdk: flutter provider: ^6.1.1 # 使用当前最新稳定版即可
修改main.dart代码,在应用顶层注册Provider:
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:note_taking_app/note_provider.dart'; import 'package:note_taking_app/ui/main_screen.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); runApp( ChangeNotifierProvider( create: (context) => NoteProvider(), child: MyApp(), ), ); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: MainScreen(), debugShowCheckedModeBanner: false, ); } }
第三步:改造MainScreen页面,移除本地状态,使用Provider托管的状态
修改后的MainScreen代码:
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:note_taking_app/constants/buttons_and_icons_misc(classes).dart'; import 'package:note_taking_app/constants/text_and_decorations(methods).dart'; import 'package:note_taking_app/note_provider.dart'; import 'package:note_taking_app/ui/adding_notes.dart'; class MainScreen extends StatefulWidget { @override _MainScreenState createState() => _MainScreenState(); } class _MainScreenState extends State<MainScreen> { @override void initState() { super.initState(); // 页面渲染完成后加载笔记列表 WidgetsBinding.instance.addPostFrameCallback((_) { Provider.of<NoteProvider>(context, listen: false).loadNotes(); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: mainScreenAppBar, floatingActionButton: FAB( whatToDo: () { gotoAddingNotesPage(context); }, ), body: SafeArea( // 监听NoteProvider状态变化,自动更新列表UI child: Consumer<NoteProvider>( builder: (context, provider, child) { if (provider.noteList.isEmpty) { return const Center(child: Text('暂无笔记,点击右下角+新建')); } return ListView.builder( itemCount: provider.noteList.length, itemBuilder: (context, index) { final note = provider.noteList[index]; return TileCard( titleText: note.title, dateText: note.date, whatToDoOnPressed: () async { final clickedNote = await provider.getNoteById(note.id); debugPrint('点击的笔记ID: ${note.id}'); // 后续跳转详情页可直接传递clickedNote }, ); }, ); }, ), ), ); } gotoAddingNotesPage(BuildContext context) { Navigator.push( context, MaterialPageRoute(builder: (context) => AddingNotes()), ); } }
第四步:改造AddingNotes页面,移除ChangeNotifier混入,调用Provider的新增方法
修改后的AddingNotes代码:
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:intl/intl.dart'; import 'package:note_taking_app/constants/buttons_and_icons_misc(classes).dart'; import 'package:note_taking_app/db/model_notes.dart'; import 'package:note_taking_app/note_provider.dart'; final bodyController = TextEditingController(); final headerController = TextEditingController(); String formattedDate = DateFormat.yMMMd('en_US').format(DateTime.now()); class AddingNotes extends StatefulWidget { @override _AddingNotesState createState() => _AddingNotesState(); } class _AddingNotesState extends State<AddingNotes> { @override void initState() { super.initState(); bodyController.clear(); headerController.clear(); } @override Widget build(BuildContext context) { final noteProvider = Provider.of<NoteProvider>(context, listen: false); return Scaffold( appBar: AppBar( elevation: 0, leading: LeadingIcon( callBack: () { Navigator.pop(context); }, ), backgroundColor: Colors.white.withOpacity(0.4), actions: <Widget>[ ActionsIconButton( icon: Icon(save, color: black), callBack: () async { String title = headerController.text.trim(); String body = bodyController.text.trim(); if (title.isEmpty && body.isEmpty) { // 可自行添加空内容提示逻辑 Navigator.pop(context); return; } // 数据库如果设置了id自增,此处传0即可,数据库会自动分配唯一ID,不要写死20 Note note = Note(0, title, body, formattedDate); await noteProvider.addNote(note); Navigator.pop(context); }, ) ], ), body: Container( color: Colors.white.withOpacity(0.4), child: Padding( padding: const EdgeInsets.all(13.0), child: Column( children: [ HeaderBody( textEditingController: headerController, ), SizedBox( height: 32.0, ), Expanded( child: NotesBody( textEditingController: bodyController, ), ), ], ), ), ), ); } }
关键注意点
- ChangeNotifier仅用于存放状态、封装操作状态的业务逻辑,不要和页面的State绑定,否则状态无法跨页面共享
- 读取状态不需要更新UI的场景使用
Provider.of<T>(context, listen: false),需要监听状态变化更新UI的场景使用Consumer包裹对应UI - 所有修改状态的操作执行完成后都需要调用
notifyListeners(),上述代码已经在loadNotes方法中统一调用,后续新增删除修改笔记的逻辑只要执行完后调用loadNotes即可自动更新所有监听该状态的页面 - 不要写死笔记ID为20,会导致新增笔记ID冲突,数据库设置自增主键的情况下传0即可自动生成唯一ID
内容的提问来源于stack exchange,提问作者ghost deathrider
相关产品推荐
相关产品推荐

