You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 22:09:03