iT邦幫忙

2021 iThome 鐵人賽

DAY 13
0
Mobile Development

Flutter - 複製貼上到開發套件之旅系列 第 13

【第十三天 - 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


上一篇
【第十二天 - Flutter NetWork 網路判斷】
下一篇
【第十四天 - Flutter 官方 CodeLab Get-To-Know 活動報名教學(上)】
系列文
Flutter - 複製貼上到開發套件之旅30

尚未有邦友留言

立即登入留言