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

Flutter应用启动时SQL数据库数据无法在UI中显示问题排查

Flutter应用重启后UI无法显示数据库数据的问题

添加若干数据后关闭应用,再次重启时UI显示为空,但实际数据库并非空的。已使用FutureBuilder组件,且在启动时调用refreshAllNames方法,恳请帮忙排查代码中的问题。

我的代码

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];
  static final String id = '_id';
  static final String name = 'myName';
}

class DataModel {
  int id;
  String name;
  DataModel({required this.id, required this.name});

  DataModel copy(
          {int? id, String? title, String? description, int? isSelected}) =>
      DataModel(id: id ?? this.id, name: name ?? this.name);
  Map<String, Object?> toJson() =>
      {if (id != null) DataFields.id: id, DataFields.name: name};
  static DataModel fromJson(Map<String, Object?> json) => DataModel(
      id: json[DataFields.id] as int, name: json[DataFields.name] as String);
}

class DBHelper {
  final String myTable = 'myDataTable';
  static const String myDatabase = 'myDB.db';

  DBHelper._init();
  static final DBHelper instance = DBHelper._init();
  static Database? db;

  Future<Database> get dataBase async {
    if (db != null) {
      return db!;
    } else {
      db = await initDB(myDatabase);
      return db!;
    }
  }

  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';
    await db.execute('''CREATE TABLE $myTable (${DataFields.id} $idType,${DataFields.name} $textType)''');
  }

  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);
    } else {
      throw Exception('ID $id not found');
    }
  }

  Future<List<DataModel>> readAllNames() async {
    final db = await instance.dataBase;
    final result = await db.rawQuery('SELECT * FROM $myTable');
    final list = result.map((json) => DataModel.fromJson(json)).toList();
    if (list.isNotEmpty)
      return list;
    else
      throw Exception('Empty List');
  }

  Future<int> countAllDataRows() async {
    final db = await instance.dataBase;
    var x = await db.rawQuery('SELECT COUNT(1) FROM $myTable');
    return x.length;
  }

  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> {
  TextEditingController mc = TextEditingController();
  static late List<DataModel> mylist = <DataModel>[];
  int index = 0;
  int myindex = 0;
  bool isLoading = false;

  @override
  void initState() {
    super.initState();
    refreshNames();
  }

  @override
  void dispose() {
    mc.dispose();
    DBHelper.instance.close();
    super.dispose();
  }

  Future refreshNames() async {
    setState(() => isLoading = true);
    mylist = await DBHelper.instance.readAllNames();
    setState(() => isLoading = false);
  }

  addItem() {
    setState(() {
      if (mc.text.isNotEmpty) {
        index++;
        mylist.add(DataModel(id: index, name: mc.text));
        DBHelper.instance.create(DataModel(id: index, name: mc.text));
        refreshNames();
      }
    });
  }

  removeItem() {
    setState(() {
      if (index > 0) {
        index;
        mylist.removeAt(index);
        DBHelper.instance.delete(index);
        refreshNames();
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    Widget space = SizedBox(width: 100, height: 20, child: Text(""));
    Widget myinput = TextField(
        controller: mc,
        decoration: InputDecoration(border: OutlineInputBorder(), labelText: 'Name'));
    Widget btnAdd = ElevatedButton(onPressed: addItem, child: Text('Add'));
    Widget btnRemove = ElevatedButton(onPressed: removeItem, child: Text('Remove'));

    Widget newList = SizedBox(
        width: 800,
        height: 500,
        child: FutureBuilder<List<DataModel>?>(builder:
            (BuildContext context, AsyncSnapshot<List<DataModel>?> snapshot) {
              return ListView.separated(
                padding: const EdgeInsets.all(8),
                itemCount: mylist.length,
                itemBuilder: (BuildContext context, int index) {
                  return Container(
                    height: 100,
                    child: Center(child: Text('${mylist[index].name}')),
                  );
                },
                separatorBuilder: (BuildContext context, int index) => const Divider(),
              );
        }));

    return MaterialApp(
        home: Scaffold(
            resizeToAvoidBottomInset: false,
            body: SingleChildScrollView(scrollDirection: Axis.vertical,
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                    mainAxisSize: MainAxisSize.min,
                    children: <Widget>[
                      Container(
                          padding: const EdgeInsets.all(5),
                          child: SingleChildScrollView(
                              child: Column(
                                  mainAxisAlignment:MainAxisAlignment.spaceEvenly,
                                  children: <Widget>[
                                space,space,myinput,space,btnAdd,space,btnRemove,space,newList,space
                              ])))
                    ]))));
  }
}

问题排查与修复方案

1. FutureBuilder未正确使用

当前FutureBuilder完全未利用自身的snapshot数据,而是直接依赖全局mylist,违背组件设计逻辑。需绑定异步任务并根据snapshot状态处理UI:

Widget newList = SizedBox(
  width: 800,
  height: 500,
  child: FutureBuilder<List<DataModel>>(
    future: DBHelper.instance.readAllNames(),
    builder: (BuildContext context, AsyncSnapshot<List<DataModel>> snapshot) {
      if (snapshot.connectionState == ConnectionState.waiting) {
        return Center(child: CircularProgressIndicator());
      } else if (snapshot.hasError) {
        return Center(child: Text('加载失败: ${snapshot.error}'));
      } else if (!snapshot.hasData || snapshot.data!.isEmpty) {
        return Center(child: Text('暂无数据'));
      } else {
        final mylist = snapshot.data!;
        return ListView.separated(
          padding: const EdgeInsets.all(8),
          itemCount: mylist.length,
          itemBuilder: (BuildContext context, int index) {
            return Container(
              height: 100,
              child: Center(child: Text('${mylist[index].name}')),
            );
          },
          separatorBuilder: (BuildContext context, int index) => const Divider(),
        );
      }
    },
  ),
);

2. readAllNames方法抛出异常导致加载失败

数据库为空时该方法抛出异常,会直接中断数据加载流程,应改为返回空列表:

Future<List<DataModel>> readAllNames() async {
  final db = await instance.dataBase;
  final result = await db.rawQuery('SELECT * FROM $myTable');
  return result.map((json) => DataModel.fromJson(json)).toList();
}

3. 自定义index与数据库自增ID冲突

手动维护的index和数据库自动生成的ID不一致,导致重启后数据匹配错误。插入时不要传入ID,让数据库自动生成:

// 修改DataModel的toJson方法
Map<String, Object?> toJson() => {
  DataFields.name: name
};

// 修改addItem方法
addItem() async {
  if (mc.text.isNotEmpty) {
    final newItem = DataModel(id: 0, name: mc.text);
    await DBHelper.instance.create(newItem);
    await refreshNames();
    mc.clear();
  }
}

4. removeItem方法逻辑错误

原代码索引操作存在越界风险,应根据列表实际数据的ID进行删除:

removeItem() async {
  if (mylist.isNotEmpty) {
    final lastItem = mylist.last;
    await DBHelper.instance.delete(lastItem.id);
    await refreshNames();
  }
}

5. mylist定义为static导致状态异常

static修饰的变量会脱离组件状态管理,改为普通成员变量:

late List<DataModel> mylist = <DataModel>[];

内容的提问来源于stack exchange,提问作者DktPhl2018

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 09:32:34