sqlite - 当获取数据的 Sqlite 数据库获取新数据时,如何更新 FutureBuilder?
问题描述
我正在使用 FutureBuilder 来显示来自 sqlite 数据库的数据。我可以使用也在该页面上的抽屉向该数据库添加数据。当我添加新数据时,我希望 FutureBuilder 自动更新,以便页面将显示刚刚添加到 sqlite 数据库的新数据。我怎么能正确地这样做呢?谢谢!
使用 FutureBuilder 显示数据的页面
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[200],
appBar: PreferredSize(
preferredSize: Size.fromHeight(95.0),
child: AppBar(
automaticallyImplyLeading: false, // hides leading widget
flexibleSpace: DataAppBar(),
),
),
body: FutureBuilder<dynamic>(
future: DataDBProvider.dataDB.getData(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
print(
'${snapshot.error}',
);
}
}
List data = snapshot.data;
return ListView.builder(
itemCount: data.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 1.0, horizontal: 4.0),
child: Card(
color: (index % 2 == 0) ? greycolor : Colors.white,
child: Container(
height: 60,
padding: EdgeInsets.fromLTRB(0, 20, 0, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Flexible(
flex: 3,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(left: 4),
child: Text(data[index].date,
style: TextStyle(fontSize: 14),
textAlign: TextAlign.left),
),
],
),
),
Expanded(
flex: 5,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(data[index].title,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.black,
fontFamily: 'Montserrat'),
textAlign: TextAlign.center)
],
)),
],
),),
Expanded(
flex: 3,
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('\$${data[index].amount}',
style: TextStyle(fontSize: 17,
color: Colors.black),
textAlign: TextAlign.right),
],
),
),
],
)),
),
);
},
);
}
));
}
解决方案
你可以在下面复制粘贴运行完整代码
来实现自动更新,这样页面就会显示新的数据
。这种情况下,你可以使用包https://pub.dev/packages/sqlbrite并且StreamBuilder
sqlbrite
是 Streaming sqflite
,BriteDatabase.createQuery
方法类似Database.query
。听返回的Stream<Query>
,它将立即通知Query
运行。
并且页面会自动显示新的数据
代码片段
class AppDb {
...
final _dbFuture = _open().then((db) => BriteDatabase(db));
Stream<List<Item>> getAllItems() async* {
final db = await _dbFuture;
yield* db
.createQuery(_tableItems, orderBy: 'createdAt DESC')
.mapToList((json) => Item.fromJson(json));
}
Future<bool> insert(Item item) async {
final db = await _dbFuture;
final id = await db.insert(
_tableItems,
item.toJson(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
return id != -1;
}
Future<bool> remove(Item item) async {
...
}
Future<bool> update(Item item) async {
...
}
}
...
StreamBuilder<List<Item>>(
stream: AppDb.getInstance().getAllItems(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
final items = snapshot.data;
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
工作演示
完整代码
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:sqlbrite/sqlbrite.dart';
import 'dart:math';
const _tableItems = 'items';
Future<Database> _open() async {
final directory = await getApplicationDocumentsDirectory();
final path = join(directory.path, 'example.db');
return await openDatabase(
path,
version: 1,
onCreate: (Database db, int version) async {
await db.execute(
'''
CREATE TABLE $_tableItems(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
content TEXT NOT NULL,
createdAt TEXT NOT NULL
)
''',
);
final batch = db.batch();
for (int i = 0; i < 10; i++) {
batch.insert(
_tableItems,
Item(
null,
contents.random(),
DateTime.now(),
).toJson(),
);
}
final list = await batch.commit(
continueOnError: true,
noResult: false,
);
print('Batch result: $list');
},
);
}
class AppDb {
static AppDb _singleton;
AppDb._();
factory AppDb.getInstance() => _singleton ??= AppDb._();
final _dbFuture = _open().then((db) => BriteDatabase(db));
Stream<List<Item>> getAllItems() async* {
final db = await _dbFuture;
yield* db
.createQuery(_tableItems, orderBy: 'createdAt DESC')
.mapToList((json) => Item.fromJson(json));
}
Future<bool> insert(Item item) async {
final db = await _dbFuture;
final id = await db.insert(
_tableItems,
item.toJson(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
return id != -1;
}
Future<bool> remove(Item item) async {
final db = await _dbFuture;
final rows = await db.delete(
_tableItems,
where: 'id = ?',
whereArgs: [item.id],
);
return rows > 0;
}
Future<bool> update(Item item) async {
final db = await _dbFuture;
final rows = await db.update(
_tableItems,
item.toJson(),
where: 'id = ?',
whereArgs: [item.id],
conflictAlgorithm: ConflictAlgorithm.replace,
);
return rows > 0;
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData.dark(),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
final _dateFormatter = DateFormat.Hms().add_yMMMd();
MyHomePage({Key key}) : super(key: key);
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('sqlbrite example'),
),
body: Container(
constraints: BoxConstraints.expand(),
child: StreamBuilder<List<Item>>(
stream: AppDb.getInstance().getAllItems(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
final items = snapshot.data;
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return ListTile(
title: Text(item.content),
subtitle:
Text('Created: ${_dateFormatter.format(item.createdAt)}'),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.remove_circle),
onPressed: () => _remove(item),
),
IconButton(
icon: Icon(Icons.edit),
onPressed: () => _update(item),
),
],
),
);
},
);
},
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: _add,
),
);
}
void _add() async {
final item = Item(
null,
contents.random(),
DateTime.now(),
);
final success = await AppDb.getInstance().insert(item);
print('Add: $success');
}
void _remove(Item item) async {
final success = await AppDb.getInstance().remove(item);
print('Remove: $success');
}
void _update(Item item) async {
final success = await AppDb.getInstance().update(
item.copyWith(
contents.random(),
),
);
print('Update: $success');
}
}
const contents = [
'Aaren',
'Aarika',
'Abagael',
'Abagail',
'Abbe',
'Abbey',
'Abbi',
'Abbie',
'Abby',
'Abbye',
'Abigael',
'Abigail',
'Abigale',
'Abra',
'Ada',
'Adah',
'Adaline',
'Adan',
'Adara',
'Adda',
'Addi',
'Addia',
'Addie',
'Addy',
'Adel',
'Adela',
'Adelaida',
'Adelaide',
'Adele',
'Adelheid',
'Adelice',
'Adelina',
'Adelind',
'Adeline',
'Adella',
'Adelle',
'Adena',
'Adey',
'Adi',
'Adiana',
'Adina',
'Adora',
'Adore',
'Adoree',
'Adorne',
'Adrea',
'Adria',
'Adriaens',
'Adrian',
'Adriana',
'Adriane',
'Adrianna',
'Adrianne',
'Adriena',
'Adrienne',
'Aeriel',
'Aeriela',
'Aeriell',
'Afton',
'Ag',
'Agace',
'Agata',
'Agatha',
'Agathe',
'Aggi',
'Aggie',
'Aggy',
'Agna',
'Agnella',
'Agnes',
'Agnes',
];
extension RandomElementExtension<T> on List<T> {
T random() {
final index = Random().nextInt(length);
return this[index];
}
}
class Item {
final int id;
final String content;
final DateTime createdAt;
const Item(
this.id,
this.content,
this.createdAt,
);
factory Item.fromJson(Map<String, dynamic> json) {
return Item(
json['id'],
json['content'],
DateTime.parse(json['createdAt']),
);
}
Map<String, dynamic> toJson() {
return {
if (id != null) 'id': id,
'content': content,
'createdAt': createdAt.toIso8601String(),
};
}
Item copyWith(String content) => Item(id, content, createdAt);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Item &&
runtimeType == other.runtimeType &&
id == other.id &&
content == other.content &&
createdAt == other.createdAt;
@override
int get hashCode => id.hashCode ^ content.hashCode ^ createdAt.hashCode;
@override
String toString() =>
'Item{id: $id, content: $content, createdAt: $createdAt}';
}
推荐阅读
- mysql - 如何优化与 group by 和 order by 相关的查询
- symfony - Symfony 3:如何在表单集合中分别提交表单?
- c# - 比较两个数组并返回一个整数数组,其值是比较的结果
- python - 在python的邻接矩阵中打印图的所有边
- sql - 如果另一个表中的值与第一个表中的值匹配,则从一个表中选择
- json - 使用 jq 通过包含字符串来查找数组元素
- javascript - hasOwnProperty 不是 Node.js 中的函数?
- javascript - 脚本执行时擦除脚本标签文本
- c# - 使用c#从一个csv文件中按值拆分多个csv文件
- c - c函数返回两种可能的类型之一