使用 Flutter 和 Provider 实现的书籍 CRUD(创建、读取、更新、删除)示例

以下是一个使用 Flutter 和 Provider 实现的书籍 CRUD(创建、读取、更新、删除)示例,数据保存在本地存储中,使用了 Hive 作为本地数据库。

项目结构

  • main.dart:应用入口文件,初始化 Hive 和 Provider。

  • book_model.dart:定义书籍模型。

  • book_provider.dart:管理书籍数据的 Provider。

  • book_list_screen.dart:显示书籍列表的界面。

  • book_form_screen.dart:添加或编辑书籍的表单界面。

1. 添加依赖

pubspec.yaml 文件中添加以下依赖:

yaml

复制

dependencies:
  flutter:
    sdk: flutter
  hive: ^2.2.3
  hive_flutter: ^1.1.0
  provider: ^6.0.5
  path_provider: ^2.0.14

dev_dependencies:
  hive_generator: ^2.0.1
  build_runner: ^2.3.3

2. 初始化 Hive

main.dart 文件中初始化 Hive:

dart

复制

import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'book_provider.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Hive.initFlutter();
  Hive.registerAdapter(BookAdapter()); // 注册书籍模型适配器
  await Hive.openBox<Book>('books'); // 打开书籍数据存储的盒子
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => BookProvider(),
      child: MaterialApp(
        title: 'Book CRUD Demo',
        home: BookListScreen(),
      ),
    );
  }
}

3. 定义书籍模型

创建 book_model.dart 文件:

dart

复制

import 'package:hive/hive.dart';

part 'book_model.g.dart';

@HiveType(typeId: 0)
class Book extends HiveObject {
  @HiveField(0)
  String title;

  @HiveField(1)
  String author;

  Book({required this.title, required this.author});
}

运行以下命令生成适配器代码:

bash

复制

flutter packages pub run build_runner build

4. 创建书籍数据 Provider

创建 book_provider.dart 文件:

dart

复制

import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'book_model.dart';

class BookProvider with ChangeNotifier {
  final Box<Book> _bookBox = Hive.box<Book>('books');

  List<Book> get books => _bookBox.values.toList();

  void addBook(Book book) {
    _bookBox.add(book);
    notifyListeners();
  }

  void updateBook(Book book) {
    book.save();
    notifyListeners();
  }

  void deleteBook(Book book) {
    book.delete();
    notifyListeners();
  }
}

5. 创建书籍列表界面

创建 book_list_screen.dart 文件:

dart

复制

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'book_form_screen.dart';
import 'book_provider.dart';

class BookListScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Books'),
      ),
      body: Consumer<BookProvider>(
        builder: (context, provider, child) {
          return ListView.builder(
            itemCount: provider.books.length,
            itemBuilder: (context, index) {
              Book book = provider.books[index];
              return ListTile(
                title: Text(book.title),
                subtitle: Text(book.author),
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => BookFormScreen(book: book),
                    ),
                  );
                },
                trailing: IconButton(
                  icon: Icon(Icons.delete),
                  onPressed: () {
                    provider.deleteBook(book);
                  },
                ),
              );
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => BookFormScreen()),
          );
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

6. 创建书籍表单界面

创建 book_form_screen.dart 文件:

dart

复制

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'book_model.dart';
import 'book_provider.dart';

class BookFormScreen extends StatelessWidget {
  final Book? book;
  final _formKey = GlobalKey<FormState>();
  final _titleController = TextEditingController();
  final _authorController = TextEditingController();

  BookFormScreen({this.book});

  @override
  void initState() {
    super.initState();
    if (book != null) {
      _titleController.text = book!.title;
      _authorController.text = book!.author;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(book != null ? 'Edit Book' : 'Add Book'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          key: _formKey,
          child: Column(
            children: [
              TextFormField(
                controller: _titleController,
                decoration: InputDecoration(labelText: 'Title'),
                validator: (value) => value!.isEmpty ? 'Please enter a title' : null,
              ),
              TextFormField(
                controller: _authorController,
                decoration: InputDecoration(labelText: 'Author'),
                validator: (value) => value!.isEmpty ? 'Please enter an author' : null,
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {
                  if (_formKey.currentState!.validate()) {
                    if (book != null) {
                      book!.title = _titleController.text;
                      book!.author = _authorController.text;
                      Provider.of<BookProvider>(context, listen: false).updateBook(book!);
                    } else {
                      Provider.of<BookProvider>(context, listen: false).addBook(
                        Book(title: _titleController.text, author: _authorController.text),
                      );
                    }
                    Navigator.pop(context);
                  }
                },
                child: Text(book != null ? 'Update' : 'Add'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

7. 运行应用

运行应用后,你可以通过点击 FloatingActionButton 添加书籍,点击书籍条目编辑书籍,点击删除图标删除书籍。所有数据都会保存在本地的 Hive 数据库中。

以上代码实现了一个完整的书籍 CRUD 应用,使用了 Hive 作为本地存储,通过 Provider 管理状态。

逐步解释一下上面的代码,更好地理解每个部分的作用。

1. 项目结构

  • main.dart:应用的入口文件,负责初始化 Hive 数据库和 Provider,并设置应用的根页面。

  • book_model.dart:定义了书籍模型(Book 类),并使用 Hive 的注解生成适配器代码。

  • book_provider.dart:管理书籍数据的逻辑,通过 Provider 提供数据的增删改查功能。

  • book_list_screen.dart:显示书籍列表的界面,用户可以查看书籍列表、编辑书籍或删除书籍。

  • book_form_screen.dart:添加或编辑书籍的表单界面,用户可以输入书籍的标题和作者。

2. 代码详细解释

main.dart

dart

复制

import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'book_provider.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized(); // 确保 Flutter 的初始化完成
  await Hive.initFlutter(); // 初始化 Hive 数据库
  Hive.registerAdapter(BookAdapter()); // 注册书籍模型的适配器
  await Hive.openBox<Book>('books'); // 打开一个名为 'books' 的 Hive 数据盒
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => BookProvider(), // 创建一个 BookProvider 实例
      child: MaterialApp(
        title: 'Book CRUD Demo',
        home: BookListScreen(), // 设置应用的根页面为书籍列表界面
      ),
    );
  }
}
  • WidgetsFlutterBinding.ensureInitialized():确保 Flutter 的初始化完成,这是在使用异步操作时的必要步骤。

  • Hive.initFlutter():初始化 Hive 数据库,使其可以在 Flutter 应用中使用。

  • Hive.registerAdapter(BookAdapter()):注册一个适配器,让 Hive 可以存储自定义的 Book 类型数据。

  • Hive.openBox<Book>('books'):打开一个名为 books 的 Hive 数据盒,用于存储书籍数据。

  • ChangeNotifierProvider:通过 Provider 包,将 BookProvider 提供给整个应用,以便在不同页面之间共享书籍数据。

book_model.dart

dart

复制

import 'package:hive/hive.dart';

part 'book_model.g.dart'; // 引入生成的适配器代码

@HiveType(typeId: 0) // 定义 Hive 类型 ID
class Book extends HiveObject {
  @HiveField(0) // 定义字段 ID
  String title;

  @HiveField(1) // 定义字段 ID
  String author;

  Book({required this.title, required this.author});
}
  • @HiveType(typeId: 0):为 Book 类指定一个唯一的类型 ID,Hive 使用这个 ID 来存储和检索数据。

  • @HiveField(0)@HiveField(1):分别为 titleauthor 字段指定字段 ID,Hive 使用这些 ID 来序列化和反序列化数据。

  • HiveObject:继承自 HiveObject,这样 Book 类就可以直接与 Hive 数据库交互(例如保存和删除)。

book_provider.dart

dart

复制

import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'book_model.dart';

class BookProvider with ChangeNotifier {
  final Box<Book> _bookBox = Hive.box<Book>('books'); // 打开书籍数据盒

  List<Book> get books => _bookBox.values.toList(); // 获取所有书籍数据

  void addBook(Book book) {
    _bookBox.add(book); // 添加书籍到数据盒
    notifyListeners(); // 通知监听者数据已更新
  }

  void updateBook(Book book) {
    book.save(); // 更新书籍数据
    notifyListeners(); // 通知监听者数据已更新
  }

  void deleteBook(Book book) {
    book.delete(); // 删除书籍
    notifyListeners(); // 通知监听者数据已更新
  }
}
  • Hive.box<Book>('books'):获取一个名为 books 的 Hive 数据盒,用于存储 Book 类型的数据。

  • _bookBox.values.toList():将数据盒中的所有书籍数据转换为一个列表。

  • addBook:将一个新的书籍对象添加到数据盒中,并通过 notifyListeners() 通知所有监听者数据已更新。

  • updateBook:调用 Book 对象的 save() 方法,更新数据盒中的书籍数据。

  • deleteBook:调用 Book 对象的 delete() 方法,从数据盒中删除书籍数据。

book_list_screen.dart

dart

复制

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'book_form_screen.dart';
import 'book_provider.dart';

class BookListScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Books'),
      ),
      body: Consumer<BookProvider>(
        builder: (context, provider, child) {
          return ListView.builder(
            itemCount: provider.books.length, // 获取书籍列表的长度
            itemBuilder: (context, index) {
              Book book = provider.books[index]; // 获取当前书籍
              return ListTile(
                title: Text(book.title), // 显示书籍标题
                subtitle: Text(book.author), // 显示书籍作者
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => BookFormScreen(book: book), // 编辑书籍
                    ),
                  );
                },
                trailing: IconButton(
                  icon: Icon(Icons.delete), // 删除按钮
                  onPressed: () {
                    provider.deleteBook(book); // 删除书籍
                  },
                ),
              );
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => BookFormScreen()), // 添加书籍
          );
        },
        child: Icon(Icons.add),
      ),
    );
  }
}
  • Consumer<BookProvider>:使用 Provider 包的 Consumer 小部件来监听 BookProvider 的变化,并根据书籍数据更新 UI。

  • ListView.builder:构建一个书籍列表,动态显示所有书籍的标题和作者。

  • onTap:点击书籍条目时,跳转到 BookFormScreen 页面,用于编辑书籍。

  • IconButton:点击删除按钮时,调用 BookProviderdeleteBook 方法删除书籍。

  • FloatingActionButton:点击添加按钮时,跳转到 BookFormScreen 页面,用于添加新书籍。

book_form_screen.dart

dart

复制

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'book_model.dart';
import 'book_provider.dart';

class BookFormScreen extends StatelessWidget {
  final Book? book; // 当前编辑的书籍对象(如果有的话)
  final _formKey = GlobalKey<FormState>(); // 表单状态管理
  final _titleController = TextEditingController(); // 标题输入框控制器
  final _authorController = TextEditingController(); // 作者输入框控制器

  BookFormScreen({this.book});

  @override
  void initState() {
    super.initState();
    if (book != null) {
      _titleController.text = book!.title; // 如果是编辑书籍,初始化标题输入框
      _authorController.text = book!.author; // 初始化作者输入框
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(book != null ? 'Edit Book' : 'Add Book'), // 标题根据操作显示
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          key: _formKey,
          child: Column(
            children: [
              TextFormField(
                controller: _titleController, // 绑定标题输入框控制器
                decoration: InputDecoration(labelText: 'Title'), // 输入框提示
                validator: (value) => value!.isEmpty ? 'Please enter a title' : null, // 验证标题是否为空
              ),
              TextFormField(
                controller: _authorController, // 绑定作者输入框控制器
                decoration: InputDecoration(labelText: 'Author'), // 输入框提示
                validator: (value) => value!.isEmpty ? 'Please enter an author' : null, // 验证作者是否为空
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {
                  if (_formKey.currentState!.validate()) { // 验证表单是否通过
                    if (book != null) {
                      book!.title = _titleController.text; // 更新书籍标题
                      book!.author = _authorController.text; // 更新

ChangeNotifierProvider(详细教程和例子

ChangeNotifierProvider 是 Flutter 中 provider 包提供的一个工具,用于在应用中管理状态。它结合了 ChangeNotifierProvider 的功能,允许开发者在多个组件之间共享和管理状态,并在状态发生变化时自动更新 UI。

ChangeNotifierProvider 的基本使用方法

ChangeNotifierProvider 的使用通常包括以下步骤:

  1. 创建一个状态管理类:继承自 ChangeNotifier,并在类中定义状态和操作状态的方法。

  2. 在应用顶层提供状态:使用 ChangeNotifierProvider 将状态管理类的实例注入到应用中。

  3. 在组件中访问和使用状态:通过 Provider.ofConsumercontext.watch 等方式访问状态。

示例:计数器应用

以下是一个简单的计数器应用示例,展示如何使用 ChangeNotifierProvider 管理状态。

1. 添加依赖

pubspec.yaml 文件中添加 provider 依赖:

yaml

复制

dependencies:
  flutter:
    sdk: flutter
  provider: ^6.0.0

然后运行 flutter pub get

2. 创建状态管理类

创建一个 Counter 类,继承自 ChangeNotifier,用于管理计数器的状态:

dart

复制

import 'package:flutter/material.dart';

class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners(); // 通知监听者状态已更改
  }
}

notifyListeners() 方法用于通知所有监听该状态的组件,触发它们的重建。

3. 在应用顶层提供状态

main.dart 文件中,使用 ChangeNotifierProviderCounter 实例注入到应用中:

dart

复制

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter.dart'; // 假设 Counter 类定义在这个文件中

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => Counter(), // 创建 Counter 实例
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: CounterScreen(),
    );
  }
}

create 参数用于创建 ChangeNotifier 的实例。

4. 在组件中访问和使用状态

在组件中,使用 Consumercontext.watch 等方式访问状态:

dart

复制

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter.dart';

class CounterScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter ChangeNotifier Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Consumer<Counter>(
              builder: (context, counter, child) => Text(
                '${counter.count}',
                style: Theme.of(context).textTheme.headline4,
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          final counter = Provider.of<Counter>(context, listen: false);
          counter.increment();
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
  • Consumer<Counter>:用于访问 Counter 实例的状态,并在状态变化时自动更新 UI。

  • Provider.of<Counter>(context, listen: false):用于访问 Counter 实例的方法,listen: false 表示不监听状态变化。

多个 Provider 的使用

如果需要管理多个状态,可以使用 MultiProvider

dart

复制

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => Counter()),
        ChangeNotifierProvider(create: (_) => AnotherModel()),
      ],
      child: MyApp(),
    ),
  );
}

总结

ChangeNotifierProvider 是一种简单且强大的状态管理方式,特别适用于需要在多个组件之间共享和管理状态的场景。通过结合 ChangeNotifierProvider,可以轻松实现状态的全局共享和动态更新。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值