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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 16:49:54