【问题标题】:Getting data from Firebase offline database (Flutter)从 Firebase 离线数据库中获取数据 (Flutter)
【发布时间】:2020-04-21 23:39:56
【问题描述】:

我有一个应用程序,它在到达第二页并根据此信息构建 ui 时仅检查一个值是否存在,如果我以常规方式执行此操作,则需要时间并在页面前一秒出现空错误还构建了我必须比我想要的更频繁地读取数据库,所以有没有办法在连接到互联网时仅从离线数据库中检查数据?

编辑:

总而言之,我有一个主页,它显示整个列表属于使用 ListView.Builder 记录为收藏的用户,用户只能看到并能够从该屏幕中删除它们,添加任何内容到列表的唯一方法是从我的第二页,我在构建时检查是否添加了项目,如果它添加了我最喜欢的按钮形状更改,如果它不是同一个按钮,则在按下时将其删除,将其保存到收藏夹。您可以查看以下代码(相关部分)

主页:

class MyFirstPage extends StatefulWidget {
  MyFirstPage({Key key, this.auth, this.userId, this.logoutCallback, this.user})
      : super(key: key);

  final BaseAuth auth;
  final VoidCallback logoutCallback;
  final String userId;
  final FirebaseUser user;

  static String routeName = "/MyFirstPage";

  @override
  _MyFirstPageState createState() => new _MyFirstPageState();
}

class _MyFirstPageState extends State<MyFirstPage> {
  String userId;
  List<Movies> _moviesList;
  Query _moviesQuery;

  final FirebaseDatabase _database = FirebaseDatabase.instance;
  final GlobalKey<FormState> formKey = GlobalKey<FormState>();
  StreamSubscription<Event> _onMoviesAddedSubscription;
  StreamSubscription<Event> _onMoviesChangedSubscription;
  AuthStatus authStatus;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
     _moviesList = new List();
    _moviesQuery = _database
        .reference()
        .child("movies")
        .orderByChild("userId")
        .equalTo(widget.userId);
    _onMoviesAddedSubscription = _moviesQuery.onChildAdded.listen(onEntryAdded);
    _onMoviesChangedSubscription =
        _moviesQuery.onChildChanged.listen(onEntryChanged);
    widget.auth.getCurrentUser().then((user) {
      setState(() {
        if (user != null) {
          userId = user?.uid;
        }
        authStatus =
        user?.uid == null ? AuthStatus.NOT_LOGGED_IN : AuthStatus.LOGGED_IN;
      });
    });

  }


  void dispose() {
    _onMoviesAddedSubscription.cancel();
    _onMoviesChangedSubscription.cancel();
    super.dispose();
  }

  onEntryChanged(Event event) {
    var oldEntry = _moviesList.singleWhere((entry) {
      return entry.key == event.snapshot.key;
    });

    setState(() {
      _moviesList[_moviesList.indexOf(oldEntry)] =
          Movies.fromSnapshot(event.snapshot);
    });
  }

  onEntryAdded(Event event) {
    setState(() {
      _moviesList.add(Movies.fromSnapshot(event.snapshot));
    });
  }

  signOut() async {
    try {
      await widget.auth.signOut();
      widget.logoutCallback();
    } catch (e) {
      print(e);
    }
  }

  updateMovies(Movies movies) {
    //Toggle completed
    movies.watched = !movies.watched;
    if (movies != null) {
      _database.reference().child("movies").set(movies.toJson());
    }
  }

  deleteMovies(String moviesId, int index) {
    _database.reference().child("movies").child(moviesId).remove().then((_) {
      print("Delete $moviesId successful");
      setState(() {
        _moviesList.removeAt(index);
      });
    });
  }

...
ListView.builder(
        shrinkWrap: true,
        scrollDirection: Axis.horizontal,
        itemCount: _moviesList.length,
        itemBuilder: (BuildContext context, int index) {...}

第二页:

class MovieDetailPage extends StatefulWidget {
  MovieDetailPage({Key key, this.title, this.auth, this.userId, this.user})
      : super(key: key);

  final BaseAuth auth;
  final String userId;
  final FirebaseUser user;
  static String routeName = "/MovieDetailPage";
  final String title;

  @override
  _MovieDetailPageState createState() => _MovieDetailPageState();}

class _MovieDetailPageState extends State<MovieDetailPage> {
  final GlobalKey<InnerDrawerState> _innerDrawerKey = GlobalKey<InnerDrawerState>();
  final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
  new GlobalKey<RefreshIndicatorState>();

  Future<void> recorded(String idname) async{
    final dbRef = await FirebaseDatabase.instance.reference().child(
        "movies").orderByChild("unique").equalTo(idname).once();
    return dbRef;
  }
  String userId;
  final FirebaseDatabase _database = FirebaseDatabase.instance;
  final GlobalKey<FormState> formKey = GlobalKey<FormState>();
  List<Movies> _moviesList;
  AuthStatus authStatus;


  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      WidgetsBinding.instance.addPostFrameCallback((_) =>
          getNamePreferences().then(updateName));
    });
    _moviesList = new List();
    widget.auth.getCurrentUser().then((user) {
      if (user != null) {
        userId = user?.uid;
      }});

  }

//I have a FutureBuilder for getting details

FutureBuilder<Payload>(
          future:  getData(_name),
          builder: (context, snapshot) {
            if (snapshot.connectionState != ConnectionState.done && firstRun == true)
              return Center(child: Image.asset("images/dice.gif"));
            if (snapshot.connectionState == ConnectionState.done)
              if (snapshot.hasError)
              {....

// and I have a second FutureBuilder for my FAB because its change according to data
                  floatingActionButton:  FutureBuilder(
                          future: recorded(userId+snapshot.data.imdbId),
                          builder: (context, snapshots) {
                            return Container(
                              width: MediaQuery.of(context).size.width,
                              child: Row(
                                mainAxisAlignment: MainAxisAlignment.end,
                                children: <Widget>[
                                  FloatingActionButton(
                                      heroTag: 3,
                                      backgroundColor: Colors.blueGrey.withOpacity(0.4),
                                      onPressed: ()
                                      {
                                        Movies movies = new Movies(snapshot.data.imdbId, snapshot.data.originalTitle, snapshot.data.posterPath, snapshot.data.backdropPath, snapshot.data.releaseDate, snapshot.data.title, snapshot.data.voteAverage, snapshot.data.genres, false, userId, userId+snapshot.data.imdbId);
                                        snapshots.data.value == null ?
                                        _database.reference().child("movies").push().set(movies.toJson())
                                            :
                                        FirebaseDatabase.instance.reference()
                                            .child('movies')
                                            .orderByChild('unique')
                                            .equalTo(userId+snapshot.data.imdbId)
                                            .once()
                                            .then((DataSnapshot snapshot) {
                                          Map<dynamic, dynamic> children = snapshot.value;
                                          children.forEach((key, value) {
                                            FirebaseDatabase.instance.reference()
                                                .child('movies')
                                                .child(key)
                                                .remove();
                                          });
                                        });
                                        setState(() {});
                                      },
                                      child: Icon(snapshots.data.value != null ? Icons.favorite : Icons.favorite_border )
                                  )

                                ],
                              ),
                            );
                          }
                      ),

【问题讨论】:

  • 我们能看看你目前的做法吗?它们是等待未来响应的多种方式。给我们更多细节
  • 我在原始帖子中添加了更多信息和代码示例。

标签: flutter firebase-realtime-database


【解决方案1】:

我认为您正在寻找 goOffline method,它会强制 Firebase 客户端断开连接。服务器。调用该方法后,它将尝试从其本地缓存(在内存中或在磁盘上)完成读取操作,并保留待处理的写入操作队列,以便在重新连接时发送到服务器(无论是应用程序重新启动时,还是当你打电话给goOnline)。

默认情况下,Firebase 客户端会保存它所看到的所有数据的内存缓存。你可以enable disk persistence,这样它也会将该缓存写入磁盘,以便在你离线时重新启动应用程序时可以重复使用。

【讨论】:

  • 我添加了“FirebaseDatabase.instance.setPersistenceEnabled(true);”到我的主要飞镖和离线()到第二页的初始化状态和在线()到第一页的初始化状态,这对减少读取有很大帮助,但是当我转到第二页时,我仍然得到一个短暂的空错误。任何方式来忽略或按下这个短暂的空错误,我已经有空场景的功能,我之前使用过 sqlite 类型的地板数据库并且从未发生过,也许我可以使用两者并在应用程序启动时使它们同步,我有任何线索还没有。
  • 我用“if (snapshots.hasData)”处理了错误,所以它可以按我的意愿工作,谢谢。
猜你喜欢
  • 2021-03-04
  • 1970-01-01
  • 2021-02-09
  • 1970-01-01
  • 2021-04-09
  • 2020-07-15
  • 2019-04-13
  • 2021-04-08
  • 2020-07-12
相关资源
最近更新 更多