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

如何在Flutter中基于SQFlite数据库创建StreamBuilder?

实现SQFlite数据的StreamBuilder自动更新ListView

要让ListView在CRUD操作时自动更新,核心是把原有的Future<List<Message>>转换成持续推送最新数据的Stream,再用StreamBuilder监听这个流。以下是具体实现步骤:

1. 改造数据库类,添加Stream控制器

在你的数据库操作类中,新增一个StreamController来管理消息数据流,同时在每次CRUD操作后推送最新数据:

class MessageDatabase {
  Database? _db;
  // 使用broadcast控制器支持多监听者
  final StreamController<List<Message>> _messageStreamController = StreamController<List<Message>>.broadcast();

  // 对外暴露消息流
  Stream<List<Message>> get messageStream => _messageStreamController.stream;

  // 初始化数据库并推送初始数据
  Future<void> initDatabase() async {
    // 原有数据库初始化逻辑(比如打开/创建数据库)
    _db = await openDatabase(
      'messages.db',
      version: 1,
      onCreate: (db, version) async {
        await db.execute('''
          CREATE TABLE Test (
            inboxId TEXT,
            messageId TEXT PRIMARY KEY,
            sticker TEXT,
            timestamp INTEGER
          )
        ''');
      },
    );
    // 推送初始数据到流
    final initialMessages = await _fetchMessages();
    _messageStreamController.add(initialMessages);
  }

  // 原有数据查询方法,改为私有方法供内部调用
  Future<List<Message>> _fetchMessages() async {
    final List<Map<String, dynamic>> maps = await _db!.query('Test', orderBy: 'timestamp DESC');
    return List.generate(maps.length, (i) {
      return Message(
        inboxId: maps[i]['inboxId'],
        messageId: maps[i]['messageId'],
        sticker: maps[i]['sticker'],
      );
    });
  }

  // 新增消息:操作完成后推送最新数据
  Future<void> insertMessage(Message message) async {
    await _db!.insert(
      'Test',
      message.toMap(),
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
    _messageStreamController.add(await _fetchMessages());
  }

  // 更新消息:操作完成后推送最新数据
  Future<void> updateMessage(Message message) async {
    await _db!.update(
      'Test',
      message.toMap(),
      where: 'messageId = ?',
      whereArgs: [message.messageId],
    );
    _messageStreamController.add(await _fetchMessages());
  }

  // 删除消息:操作完成后推送最新数据
  Future<void> deleteMessage(String messageId) async {
    await _db!.delete(
      'Test',
      where: 'messageId = ?',
      whereArgs: [messageId],
    );
    _messageStreamController.add(await _fetchMessages());
  }

  // 销毁控制器,避免内存泄漏
  void dispose() {
    _messageStreamController.close();
  }
}

2. 实现Message类的toMap方法

确保Message类能转换成数据库存储的Map格式:

class Message {
  final String inboxId;
  final String messageId;
  final String? sticker;

  Message({
    required this.inboxId,
    required this.messageId,
    this.sticker,
  });

  // 转换为数据库可用的Map
  Map<String, dynamic> toMap() {
    return {
      'inboxId': inboxId,
      'messageId': messageId,
      'sticker': sticker,
      'timestamp': DateTime.now().millisecondsSinceEpoch,
    };
  }
}

3. 使用StreamBuilder构建ListView

在页面中监听消息流,自动更新列表:

class MessageListScreen extends StatefulWidget {
  @override
  _MessageListScreenState createState() => _MessageListScreenState();
}

class _MessageListScreenState extends State<MessageListScreen> {
  late MessageDatabase _db;

  @override
  void initState() {
    super.initState();
    _db = MessageDatabase();
    _db.initDatabase();
  }

  @override
  void dispose() {
    _db.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('消息列表')),
      body: StreamBuilder<List<Message>>(
        stream: _db.messageStream,
        builder: (context, snapshot) {
          // 加载状态
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          }
          // 无数据状态
          if (!snapshot.hasData || snapshot.data!.isEmpty) {
            return const Center(child: Text('暂无消息'));
          }
          // 渲染列表
          return ListView.builder(
            itemCount: snapshot.data!.length,
            itemBuilder: (context, index) {
              final message = snapshot.data![index];
              return ListTile(
                title: Text('消息ID: ${message.messageId}'),
                subtitle: Text('收件箱ID: ${message.inboxId}'),
                trailing: message.sticker != null ? Text('贴纸: ${message.sticker}') : null,
              );
            },
          );
        },
      ),
      // 测试用:添加新消息按钮
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _db.insertMessage(Message(
            inboxId: 'inbox_001',
            messageId: 'msg_${DateTime.now().millisecondsSinceEpoch}',
            sticker: 'smile',
          ));
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

关键注意事项

  • 使用broadcast类型的StreamController,这样多个组件可以同时监听同一个数据流。
  • 每次CRUD操作完成后必须调用_messageStreamController.add(最新数据),触发StreamBuilder重建列表。
  • 在页面销毁时调用dispose()关闭StreamController,防止内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 04:35:40