Flutter进阶—Firebase数据库实例

来源:互联网 发布:路小雨知乎 编辑:程序博客网 时间:2024/05/29 04:40

如果需要使用Firebase实时数据库。首先我们需要为项目配置Firebase,具体配置方法可以在《Flutter实战一Flutter聊天应用(五)》查看,因为我们只需要使用Firebase数据库,所以pubspec.yaml文件的内容需要修改一下。

name: talk_casuallydescription: A new flutter project.dependencies:  flutter:    sdk: flutter  firebase_database: ^0.0.4

首先我们修改项目的main.dart文件,代码如下。

import 'dart:async';import 'package:flutter/material.dart';import 'package:firebase_database/firebase_database.dart';import 'package:firebase_database/ui/firebase_animated_list.dart';void main() {  runApp(new MyApp());}class MyApp extends StatelessWidget {  @override  Widget build(BuildContext context) {    return new MaterialApp(      title: 'Flutter数据库实例',      home: new MyHomePage(),    );  }}class MyHomePage extends StatefulWidget {  @override  _MyHomePageState createState() => new _MyHomePageState();}class _MyHomePageState extends State<MyHomePage> {  @override  Widget build(BuildContext context) {    return new Scaffold(      appBar: new AppBar(        title: const Text('Flutter数据库实例'),      ),    );  }}

打开Firebase控制台,更改Firebase实时数据库的安全规则,选择“Database > 规则”,规则如下所示。

{  "rules": {    "counter":{      ".read": true,        ".write": true    },    "messages":{      ".read": true,        ".write": true    }  }}

现在我们需要使用DatabaseReference连接到Firebase数据库,DatabaseReference表示Firebase数据库中的特定位置,可用于读取或写入数据到该位置。

class _MyHomePageState extends State<MyHomePage> {  //...  final DatabaseReference _counterRef = FirebaseDatabase.instance.reference().child('counter');  final DatabaseReference _messagesRef = FirebaseDatabase.instance.reference().child('messages');  //...}

FirebaseDatabase是访问Firebase数据库的入口点,我们可以通过调用FirebaseDatabase.instance获取一个实例,要访问数据库中的位置并读取或写入数据,需要使用referencechild获取指定相对路径中的位置的DatabaseReference,相对路径可以是简单的子key(例如“fred”)或更深的斜线分隔的路径(例如“fred/name/first”)。

我们先增加一个_counter存储按钮点击次数。在默认情况下,用户的写入操作和缓存数据仅存储在内存中,并在用户重新启动应用程序时丢失。通过将启用持久性设置为true,数据将持续存储在设备(磁盘)存储上,并在应用程序重新启动时再次可用(即使当时没有网络连接)。setPersistenceEnabled能够开启数据库的持久性设置,必须在调用数据库引用方法之前设置此属性,并且每个应用程序只需要调用一次。如果操作成功,则Future将返回true,如果持久性无法设置(可能是已经创建数据库引用),则返回false

class _MyHomePageState extends State<MyHomePage> {  //...  int _counter;  //...  @override  void initState() {    super.initState();    FirebaseDatabase.instance.setPersistenceEnabled(true);  }  //...}

现在我们的应用程序中,Firebase数据库客户端将缓存同步的数据,并跟踪用户在应用程序运行时发起的所有写入。当网络连接恢复时,它可以无缝地处理间歇性网络连接并重新发送写入操作。默认情况下,Firebase数据库客户端将使用高达10MB的磁盘空间来缓存数据。如果缓存增长超过此大小,客户端将开始删除尚未使用的数据。如果发现应用程序缓存数据太少或太多,可以使用setPersistenceCacheSizeBytes来更改缓存大小。此属性必须在调用数据库引用方法之前设置,并且每个应用程序只需要调用一次。

class _MyHomePageState extends State<MyHomePage> {  //...  @override  void initState() {    super.initState();    FirebaseDatabase.instance.setPersistenceEnabled(true);    FirebaseDatabase.instance.setPersistenceCacheSizeBytes(10000000);  }  //...}

如果操作成功,则Future将返回true,如果该值无法设置,则为false(可能已经创建数据库引用)。需要注意的是,指定的高速缓存大小只是一个近似值,并且磁盘上的大小有时会暂时超过它。该属性不支持小于1MB或大于100MB的缓存大小。

class _MyHomePageState extends State<MyHomePage> {  //...  @override  void initState() {    super.initState();    FirebaseDatabase.instance.setPersistenceEnabled(true);    FirebaseDatabase.instance.setPersistenceCacheSizeBytes(10000000);    _counterRef.keepSynced(true);  }  //...}

通过在某个位置调用keepSynced(true),即使没有为该位置附加任何监听器,该位置的数据将自动下载并保持同步。另外,当一个位置保持同步时,它不会被从持久磁盘缓存中逐出。

现在我们增加两个StreamSubscription类型的变量,StreamSubscription表示来自Stream的事件的订阅。订阅将提供事件给侦听器,并保存用于处理事件的回调。订阅也可以用于取消订阅事件,或者临时暂停Stream中的事件。Stream是异步数据事件的来源,Stream提供了一种接收一系列事件的方式。每个Event都是一个数据事件,也称为流的元素,或者一个错误事件。当流已经发出其所有事件时,一个“完成”事件将通知侦听器已到达结束。我们可以监听流,使其开始生成事件,并设置接收事件的侦听器。当我们侦听时,将收到一个StreamSubscription对象,该对象是提供事件的活动对象,可以用于停止再次监听,或临时暂停订阅的事件。

class _MyHomePageState extends State<MyHomePage> {  //...  StreamSubscription<Event> _counterSubscription;  StreamSubscription<Event> _messagesSubscription;  //...}

现在需要在应用程序启动时读取按钮被点击的次数。onValue表示当该位置的数据更新时触发。Event里封装了DataSnapshot,也可能是其以前的兄弟的key,通常用于命令快照(即返回一个DataSnapshot)。DataSnapshot包含来自Firebase数据库位置的数据,每当我们读取Firebase数据时,将收到一个DataSnapshotDataSnapshotvalue属性以本机类型返回此数据快照的内容。

class _MyHomePageState extends State<MyHomePage> {  //...  @override  void initState() {    //...    _counterSubscription = _counterRef.onValue.listen((Event event) {      setState(() {        _counter = event.snapshot.value ?? 0;      });    });  }  //...}

上面代码中的双问号操作符意思是取所赋值??左边的,如果左边为null,取所赋值??右边的。应用程序在启动时还应该获取消息列表,还可以打印在控制台上。DatabaseReferencelimitToLast方法会创建一个有限制的查询并将其锚定到窗口的末尾。onChildAdded表示在子数据加入时调用。当我们使用Stream.listenStream上侦听时,会返回一个StreamSubscription对象。

class _MyHomePageState extends State<MyHomePage> {  //...  @override  void initState() {    //...    _messagesSubscription =  _messagesRef.limitToLast(10).onChildAdded.listen((Event event) {          print('子数据增加了: ${event.snapshot.value}');        });  }  //...}

在资源使用完之后及时关闭是一个良好的习惯,StreamSubscriptioncancel方法用于取消订阅,在此调用之后,订阅不再接收事件。流可能需要关闭事件源,并在订阅取消后进行清理。

class _MyHomePageState extends State<MyHomePage> {  //...  @override  void dispose() {    super.dispose();    _messagesSubscription.cancel();    _counterSubscription.cancel();  }  //...}

修改build的内容,使我们可以看到查看按钮被点击了多少次。

class _MyHomePageState extends State<MyHomePage> {  //...  @override  Widget build(BuildContext context) {    return new Scaffold(      appBar: new AppBar(        title: const Text('Flutter数据库实例'),      ),      body: new Column(        children: <Widget>[          new Flexible(            child: new Center(              child: new Text(                '按钮点击 $_counter 次,\n\n该统计包括所有的终端!',              ),            ),          ),        ],      ),    );  }  //...}

再增加一个_anchorToBottom变量,控制显示消息列表的样式。

class _MyHomePageState extends State<MyHomePage> {  //...  bool _anchorToBottom = false;  //...}

修改build的内容,增加一个可选项,按钮如何显示消息列表。

class _MyHomePageState extends State<MyHomePage> {  //...  @override  Widget build(BuildContext context) {    return new Scaffold(      //...      body: new Column(        children: <Widget>[          //...          new ListTile(            leading: new Checkbox(              onChanged: (bool value) {                setState(() {                  _anchorToBottom = value;                });              },              value: _anchorToBottom,            ),            title: const Text('锚点到底部'),          ),        ],      ),    );  }  //...}

现在可以使用FirebaseAnimatedList控件把消息列表显示出来,FirebaseAnimatedList是绑定到查询的AnimatedList控件。

class _MyHomePageState extends State<MyHomePage> {  //...  @override  Widget build(BuildContext context) {    return new Scaffold(      //...      body: new Column(        children: <Widget>[          //...          new Flexible(            child: new FirebaseAnimatedList(              key: new ValueKey<bool>(_anchorToBottom),              query: _messagesRef,              reverse: _anchorToBottom,              sort: _anchorToBottom                  ? (DataSnapshot a, DataSnapshot b) => b.key.compareTo(a.key)                  : null,              itemBuilder: (BuildContext context, DataSnapshot snapshot, Animation<double> animation) {                return new SizeTransition(                  sizeFactor: animation,                  child: new Text(snapshot.value.toString()),                );              },            ),          )        ],      ),    );  }  //...}

FirebaseAnimatedList控件中,key属性控制一个控件如何替换树中另一个控件,query属性设置用于填充动画列表的Firebase查询,reverse属性设置滚动视图是否在阅读方向滚动,sort属性设置用于在排序列表时比较快照的可选功能,itemBuilder属性根据需要调用来构建列表项控件。

我们需要编写一个提交事件,用于将数据上传到数据库。DatabaseReferenceonce方法侦听单个值事件,然后停止侦听。DatabaseReferenceset方法将值写入具有指定优先级的位置(如果适用),这将覆盖此位置的所有数据。允许写入的数据类型有StringbooleanintdoubleMapList,写入的效果将立即可见,相应的事件将被触发。如果传递新值为null,表示此位置的所有数据将被删除。

class _MyHomePageState extends State<MyHomePage> {  //...  String _kTestKey = 'Hello';  String _kTestValue = 'world!';  //...  Future<Null> _increment() async {    final DataSnapshot snapshot = await _counterRef.once();    setState(() {      _counter = (snapshot.value ?? 0) + 1;    });    _counterRef.set(_counter);    _messagesRef.push().set(<String, String>{_kTestKey: '$_kTestValue $_counter'});  }  //...}

DatabaseReferencepush方法使用唯一key生成新的子位置并返回一个DatabaseReference。当Firebase数据库位置的子位置是一个列表时,这个方法非常有用。由childByAutoId生成的唯一key以客户端生成的时间戳为前缀,以便生成的列表将按时间顺序排序。

最后我们修改build的内容,增加一个浮动按钮,并将上面编写的事件作为其点击事件。

class _MyHomePageState extends State<MyHomePage> {  //...  @override  Widget build(BuildContext context) {    return new Scaffold(      appBar: new AppBar(        title: const Text('Flutter数据库实例'),      ),      body: new Column(        //...      ),      floatingActionButton: new FloatingActionButton(        onPressed: _increment,        tooltip: '增加',        child: new Icon(Icons.add),      ),    );  }  //...}

启动应用程序,点击按钮三次,控制台与终端的显示如下:

这里写图片描述

这里写图片描述

数据库内容显示如下:

这里写图片描述

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 英雄联盟误删文件怎么办 拍拍贷换了号码怎么办 手机打开显示无法连接服务器怎么办 剑灵画面卡顿怎么办 cf被永久禁赛了怎么办 微信没有微游戏商店怎么办 游侠云盒下载慢怎么办2018 安卓手机玩网页游戏卡怎么办 safari点开什么都没有怎么办 康佳电视全网搜索打不开怎么办 脚被图钉扎了怎么办 电脑中毒了打不开软件怎么办 剑三程序不兼容怎么办 玩无主之地卡怎么办 平台老板跑路了怎么办 qq在苹果下载不了怎么办 下载速度快上传速度慢怎么办 苹果7开网页慢怎么办 为什么浏览器下载视频速度慢怎么办 机连WLAN网速慢怎么办 会声会影卸载后无法重新安装怎么办 电视空间不足无法卸载怎么办 堡垒之夜下载慢怎么办 手机网盘下载速度慢怎么办插件 笔记本电脑显示连接不可用怎么办 蓝魔手机充电慢怎么办 360f4手机充电慢怎么办 vivo卡了怎么办小窍门 白色衣服用84泡后变黄怎么办 用祛斑霜脱皮了怎么办 用祛斑霜脸一直蜕皮怎么办 吃热的就流鼻涕怎么办 键盘qaz失灵其他都没事怎么办 时时彩代理抓了怎么办 6p升级ios11卡顿怎么办 微信弄没了又换号了怎么办 快手账号异常请去激活怎么办 九游账号被转移怎么办 绝地求生刺激战场闪退怎么办 qq回执编号忘了怎么办 电脑开机要用户名和密码怎么办