Flutter中如何使用SQFLite保存SwitchListTile的选中状态?
问题分析与解决方案
你的核心问题是切换开关时只更新了内存中的数据,没有调用SQFlite的更新接口同步到数据库,同时代码里还有几个影响数据一致性的小问题,下面逐一修复:
1. 最关键:开关切换时同步数据库
在SwitchListTile的onChanged回调里,修改完内存中的isSelected值后,必须调用DBHelper.instance.update()把变更同步到数据库:
onChanged: (bool value) async { setState(() { // 取消其他项选中状态 for (int i = 0; i < names.length; i++) { names[i].isSelected = i == index ? (value ? 1 : 0) : 0; } mc.text = names[index].name; }); // 同步所有变更到数据库 for (var item in names) { await DBHelper.instance.update(item); } }
2. 修复添加数据时的ID不一致问题
你现在添加数据时,内存和数据库的ID生成逻辑不统一,导致后续更新找不到对应记录。应该用数据库返回的自增ID来更新内存中的数据:
addItemToList() async { if (mc.text.isNotEmpty) { // 数据库会自动生成ID,这里传0即可 final newItem = await DBHelper.instance.create( DataModel(id: 0, name: mc.text, isSelected: 0) ); setState(() { names.add(newItem); mc.clear(); }); } }
3. 修复数据库表字段定义
创建表时isSelected字段没有指定类型和默认值,可能导致存储异常,修改_createDB方法:
Future _createDB(Database db, int version) async { var idType = 'INTEGER PRIMARY KEY AUTOINCREMENT'; var textType = 'TEXT NOT NULL'; var intType = 'INTEGER DEFAULT 0'; await db.execute('''CREATE TABLE $myTable ( ${DataFields.id} $idType, ${DataFields.name} $textType, ${DataFields.selectedValue} $intType )'''); }
4. 初始化时加载数据库数据
页面启动时要把数据库里的现有数据加载到内存列表,否则重启后数据丢失:
@override void initState() { super.initState(); _loadData(); } Future<void> _loadData() async { final dataList = await DBHelper.instance.readAllData(); setState(() { names.clear(); names.addAll(dataList); }); }
5. 修复删除数据逻辑
当前删除逻辑依赖类成员index,无法准确删除目标项,改为删除选中项:
// 添加变量记录选中项index int? selectedIndex; // 修改删除方法 removeItemFromList() async { if (selectedIndex == null || names.isEmpty) return; final itemToDelete = names[selectedIndex!]; await DBHelper.instance.delete(itemToDelete.id); setState(() { names.removeAt(selectedIndex!); selectedIndex = null; }); } // 在SwitchListTile中记录选中状态 SwitchListTile( selected: selectedIndex == index, onChanged: (bool value) async { setState(() { selectedIndex = index; // ...其他逻辑 }); // ...同步数据库 } )
完整修改后的代码
import 'package:flutter/material.dart'; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; void main() {runApp(MyApp());} class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class DataFields { static final List<String> values = [id, name, selectedValue]; static final String id = '_id'; static final String name = 'myName'; static final String selectedValue='isSelected'; } class DataModel{ int id; String name; int isSelected; DataModel({required this.id, required this.name, this.isSelected = 0}); DataModel copy({int? id,String? name,int? isSelected}) => DataModel( id: id ?? this.id, name: name ?? this.name, isSelected: isSelected ?? this.isSelected ); Map<String, Object?> toJson() => { DataFields.id: id, DataFields.name: name, DataFields.selectedValue: isSelected }; static DataModel fromJson(Map<String, Object?> json) => DataModel( id: json[DataFields.id] as int, name: json[DataFields.name] as String, isSelected: json[DataFields.selectedValue] as int ); } class DBHelper { final String myTable = 'myDataTable'; static const String myDatabase = 'myDB.db'; DBHelper._init(); static final DBHelper instance = DBHelper._init(); static Database? _database; Future<Database> get dataBase async { if (_database != null) {return _database!;} _database = await _initDB(myDatabase); return _database!; } Future<Database> _initDB(String filePath) async { final dbPath = await getDatabasesPath(); final path = join(dbPath, filePath); return await openDatabase(path, version: 1, onCreate: _createDB); } Future _createDB(Database db, int version) async { var idType = 'INTEGER PRIMARY KEY AUTOINCREMENT'; var textType = 'TEXT NOT NULL'; var intType = 'INTEGER DEFAULT 0'; await db.execute('''CREATE TABLE $myTable ( ${DataFields.id} $idType, ${DataFields.name} $textType, ${DataFields.selectedValue} $intType )'''); } Future<DataModel> create(DataModel datamodel) async { final db = await instance.dataBase; final id = await db.insert(myTable, datamodel.toJson()); return datamodel.copy(id: id); } Future<DataModel> readData(int id) async { final db = await instance.dataBase; final maps = await db.query( myTable, columns: DataFields.values, where: '${DataFields.id} = ?', whereArgs: [id] ); if (maps.isNotEmpty) {return DataModel.fromJson(maps.first);} throw Exception('ID $id not found'); } Future<List<DataModel>> readAllData() async { final db = await instance.dataBase; final result = await db.rawQuery('SELECT * FROM $myTable'); return result.map((json) => DataModel.fromJson(json)).toList(); } Future<int> countAllDataRows() async { final db = await instance.dataBase; var x = await db.rawQuery('SELECT COUNT(1) FROM $myTable'); return Sqflite.firstIntValue(x) ?? 0; } Future<int> update(DataModel datamodel) async { final db = await instance.dataBase; return db.update( myTable, datamodel.toJson(), where: '${DataFields.id} = ?', whereArgs: [datamodel.id] ); } Future<int> delete(int id) async { final db = await instance.dataBase; return await db.delete( myTable, where: '${DataFields.id} = ?', whereArgs: [id] ); } Future close() async { final db = await instance.dataBase; db.close(); } } class _MyAppState extends State<MyApp> { final List<DataModel> names = <DataModel>[]; TextEditingController mc = TextEditingController(); int? selectedIndex; @override void initState() { super.initState(); _loadData(); } Future<void> _loadData() async { final dataList = await DBHelper.instance.readAllData(); setState(() { names.clear(); names.addAll(dataList); // 初始化选中项 final selectedItem = names.firstWhere((item) => item.isSelected == 1, orElse: () => names.isNotEmpty ? names[0] : DataModel(id:0,name:"")); selectedIndex = names.isNotEmpty ? names.indexOf(selectedItem) : null; }); } addItemToList() async { if (mc.text.isNotEmpty) { final newItem = await DBHelper.instance.create( DataModel(id: 0, name: mc.text, isSelected: 0) ); setState(() { names.add(newItem); mc.clear(); }); } } removeItemFromList() async { if (selectedIndex == null || names.isEmpty) return; final itemToDelete = names[selectedIndex!]; await DBHelper.instance.delete(itemToDelete.id); setState(() { names.removeAt(selectedIndex!); selectedIndex = null; }); } @override Widget build(BuildContext context) { Widget space = const SizedBox(height: 5); Widget myinput = TextField( controller: mc, decoration: const InputDecoration( border: OutlineInputBorder(), labelText: 'Name' ) ); Widget btnAdd = ElevatedButton( onPressed: addItemToList, child: const Text('Add') ); Widget btnRemove = ElevatedButton( onPressed: removeItemFromList, child: const Text('Remove') ); Widget mylist = SizedBox( width: 800, height: 500, child: ListView.builder( shrinkWrap: true, itemCount: names.length, itemBuilder: (BuildContext context, int index) { return SwitchListTile( title: Text(names[index].name, style: const TextStyle(fontSize: 11)), value: names[index].isSelected == 1, selected: selectedIndex == index, onChanged: (bool value) async { setState(() { for (int i = 0; i < names.length; i++) { names[i].isSelected = i == index ? (value ? 1 : 0) : 0; } selectedIndex = index; mc.text = names[index].name; }); for (var item in names) { await DBHelper.instance.update(item); } } ); }, ), ); return MaterialApp( home: Scaffold( resizeToAvoidBottomInset: false, body: SingleChildScrollView( child: Container( padding: const EdgeInsets.all(5), child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ space, myinput, space, btnAdd, space, btnRemove, space, mylist, space ] ) ) ) ) ); } }
额外优化说明
- 调整了命名规范,比如把
SelectedValue改成小写开头的selectedValue,符合Dart代码规范 - 修复了
countAllDataRows方法,正确返回数据库表行数 - 优化了布局结构,去掉多余的嵌套滚动组件
内容的提问来源于stack exchange,提问作者DktPhl2018
相关产品推荐
相关产品推荐

