【第十三天 - Flutter Sqflite+Provider】

前言

今日的程序码 => GITHUB
这篇将要介绍 Sqflite + Provider 的资料库范例。

范例说明

会创立一个 table,名称叫做 user,储存在应用程序目录底下的 my.db 文件里面,会有以下几个栏位

  • id(autoincrement Integer)
  • firstName(text)
  • lastName(text)
  • time(text)

新增、更新需要整笔资料的参数,删除只需要 item 的 id 的参数,取得所有资料是一个 void 的方法,

Database

class UserDB {
  static final UserDB db = UserDB();
  Database? _database;

  Future<Database> get database async {
    if (_database != null) {
      return _database!;
    }
    _database = await openDb();
    return _database!;
  }

  Future openDb() async {
    // 获取我们的应用程序目录的位置。这是我们应用程序文件的存储位置,并且仅存储我们的应用程序文件。
    // 当应用被删除时,此目录中的文件也会被删除。
    return await openDatabase(join(await getDatabasesPath(), "my.db"),
        version: 1,
        onOpen: (db) async {}, onCreate: (Database db, int version) async {
      // Create the user table
      await db.execute(
          "CREATE TABLE user(id INTEGER PRIMARY KEY autoincrement,firstName TEXT,lastName TEXT,time Text)");
    });
  }

  Future insertUserData(UserModel model) async {
    final db = await database;
    return db.insert('user', model.toMap());
  }

  Future updateUser(UserModel model) async {
    final db = await database;
    return db
        .update('user', model.toMap(), where: "id = ?", whereArgs: [model.id]);
  }

  Future<int> deleteUser(int id) async {
    final db = await database;
    return db.delete('user', where: "id = ?", whereArgs: [id]);
  }

  Future<List<UserModel>> getUser() async {
    final db = await database;
    final List<Map<String, dynamic>> maps = await db.query('user');
    List<UserModel> list = maps.isNotEmpty
        ? maps.map((note) => UserModel.fromMap(note)).toList()
        : [];

    return list;
  }

  Future close() async => db.close();
}

Add User Provider

这边的话就很吃 StreamController 的观念了

  • StreamSink = 事件的入口
  • StreamController = 即时监听的控制器
  • Stream = 用於监听,可以用 listen 来监听
  • StreamSubscription = 有点类似订阅(!?)另类的控制监听的东西,最後再不需要的时候关闭。具备 cacenl、pause
class AddUsersProvider extends ChangeNotifier {
  /// 取得所有 user 资料的 controller
  final _usersController = StreamController<List<UserModel>>.broadcast();

  Stream<List<UserModel>> get getUserStream => _usersController.stream;

  /// 新增的 controller
  final _insertController = StreamController<UserModel>.broadcast();

  /// 更新的 controller
  final _updateController = StreamController<UserModel>.broadcast();

  /// 删除的 controller
  final _deleteController = StreamController<int>.broadcast();

  /// 搜寻的 controller
  final _searchController = StreamController<String>.broadcast();

  /// Sink
  StreamSink<UserModel> get insert => _insertController.sink;

  StreamSink<UserModel> get update => _updateController.sink;

  StreamSink<int> get delete => _deleteController.sink;

  StreamSink<String> get search => _searchController.sink;

  /// 初始化
  AddUsersProvider() {
    updateScreenData();
    _updateController.stream
        .listen((userModel) => _handleUpdateUser(userModel));
    _insertController.stream.listen((userModel) => _handleAddUser(userModel));
    _deleteController.stream
        .listen((userModel) => _handleDeleteUser(userModel));
    _searchController.stream
        .listen((userModel) => _handleSearchUser(userModel));
  }

  /// 新增
  void _handleAddUser(UserModel userModel) async {
    await UserDB.db.insertUserData(userModel);
    updateScreenData();
  }

  /// 更新
  void _handleUpdateUser(UserModel userModel) async {
    await UserDB.db.updateUser(userModel);
    updateScreenData();
  }

  /// 删除
  void _handleDeleteUser(int id) async {
    await UserDB.db.deleteUser(id);
    updateScreenData();
  }

  /// 搜寻
  void _handleSearchUser(String text) async {
    List<UserModel> user = await UserDB.db.getUser();
    var listData = <UserModel>[];
    user.forEach((stud) {
      var st2 = UserModel(
          id: stud.id,
          lastName: stud.lastName,
          firstName: stud.firstName,
          time: stud.time);
      if ((st2.firstName.toLowerCase() + " " + st2.lastName.toLowerCase())
              .contains(text.toLowerCase()) ||
          st2.firstName.toLowerCase().contains(text.toLowerCase()) ||
          st2.lastName.toLowerCase().contains(text.toLowerCase()) ||
          st2.time.toLowerCase().contains(text.toLowerCase())) {
        listData.add(stud);
      }
    });
    _usersController.sink.add(listData);
  }

  /// 取得资料
  void updateScreenData() async {
    List<UserModel> users = await UserDB.db.getUser();
    _usersController.sink.add(users);
  }

  @override
  void dispose() {
    _usersController.close();
    _insertController.close();
    _deleteController.close();
    _updateController.close();
    _searchController.close();
    UserDB.db.close();
    super.dispose();
  }
}

使用 provider

初始 provider

MultiProvider(
      providers: [
        ChangeNotifierProvider<AddUsersProvider>(
          create: (context) => new AddUsersProvider(),
        ),
      ],
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.cyan,
        ),
        home: HomePage(),
      ),
    );

读取 provider 的资料

 context.read<AddUsersProvider>().search.add(changed)
 
 context.read<AddUsersProvider>().delete.add(data[index].id!)
 
 context.read<AddUsersProvider>().update.add(UserModel(
     id: userModel!.id,
     firstName: _firstNameController.text,
     lastName: _lastNameController.text,
     time: nowTime()));
         
 context.read<AddUsersProvider>().insert.add(UserModel(
     firstName: _firstNameController.text,
     lastName: _lastNameController.text,
     time: nowTime()));         

UI 画面(使用 StreamBuilder)

class ShowStream extends StatelessWidget {
@override
Widget build(BuildContext context) {
  return StreamBuilder<List<UserModel>>(
    stream: context.watch<AddUsersProvider>().getUserStream,
    builder: (BuildContext context, AsyncSnapshot<List<UserModel>> snapshot) {
      if (snapshot.hasData) {
        if (snapshot.data!.length == 0) {
          return Text('No Data');
        }
        return HomeList(data: snapshot.data!);
      }
      return Center(
        child: CircularProgressIndicator(),
      );
    },
  );
}
}

更详细的程序码请看 GITHUB


<<:  【狂贺】JACKSOFT持续通过110年经济部工业局审查,成为台湾第一家GRC (治理,风险管理与法规遵循)应用软件产品技术服务能量登录公司!

>>:  Day 14 : 基础套件的介绍-os套件,帮助你管理资料

使用 MockK 做测试

接下来的测试将会需要用到 mocking 的 library ,在 Android 大家比较常用的是...

Day 3 我要开始Mock了

Mock What's mock? 先想像一个画面,当我们要隔离我们的元件时,一个component...

Day 15 - Ping Sweeping 与 Port Scanning

出於书本 Chapter 8. Network Infrastructure 撑过一半了加油加油加油...

【DAY03】如何在VSCODE上运行HTML

步骤 先去官网下载VSCODE(这边就先只说明window版本) 网址:https://code.v...

[火锅吃到饱-11] 锅好日 Good Day Pot 个人锅物吃到饱

锅好日也是有靠山的--「昭日堂烧肉」 我很常去的天圆地方,背後有新天地撑腰;女儿红的美美心,是潮港城...