import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:todo_list/config/api_service.dart'; import 'package:todo_list/config/api_strategy.dart'; import 'package:todo_list/database/database.dart'; import 'package:todo_list/i10n/localization_intl.dart'; import 'package:todo_list/json/task_bean.dart'; import 'package:todo_list/model/main_page_model.dart'; import 'package:todo_list/utils/shared_util.dart'; class SynchronizeWidget extends StatefulWidget { final MainPageModel mainPageModel; const SynchronizeWidget({Key key, @required this.mainPageModel}) : super(key: key); @override _SynchronizeWidgetState createState() => _SynchronizeWidgetState(); } class _SynchronizeWidgetState extends State< SynchronizeWidget> { SynFlag synFlag = SynFlag.hasNotSynced; CancelToken cancelToken; String account; String token; ///表示需要同步的数量 int needSyncedLength = 0; ///表示已经同步成功的数量 List syncedList = []; ///同步成功后需要在本地进行更新的task List needSynTasks = []; bool loginSucceed = false; @override void initState() { cancelToken = CancelToken(); doLogin(); super.initState(); } @override void dispose() { if(!cancelToken.isCancelled){ cancelToken.cancel(); } super.dispose(); } @override Widget build(BuildContext context) { return InkWell( onTap: onTap, child: Container( alignment: Alignment.center, child: getSynWidget(synFlag), ), ); } void onTap(){ if(synFlag == SynFlag.noNeedSynced) return null; return loginSucceed ? clickToSyn() : doLogin(); } Widget getSynWidget(SynFlag synFlag){ switch(synFlag){ case SynFlag.hasNotSynced: return Container( width: 60, height: 80, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( width: 20, height: 20, margin: EdgeInsets.only(bottom: 5), child: CircularProgressIndicator( value: 1.0, valueColor: AlwaysStoppedAnimation(Colors.white), ), ), Text(DemoLocalizations.of(context).clickToSyn,style: TextStyle(color: Colors.white),), Text("(0 / ${needSyncedLength})",style: TextStyle(color: Colors.white, fontSize: 12),), ], ), ); break; case SynFlag.synchronizing: return Container( width: 60, height: 80, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 20, height: 20, margin: EdgeInsets.only(bottom: 5), child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(Colors.white), ), ), Text(DemoLocalizations.of(context).synchronizing,style: TextStyle(color: Colors.white),), Text("(${needSynTasks.length} / ${needSyncedLength})",style: TextStyle(color: Colors.white, fontSize: 12),), ], ), ); break; case SynFlag.cloudSynchronizing: return Container( width: 60, height: 80, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 20, height: 20, margin: EdgeInsets.only(bottom: 5), child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(Colors.white), ), ), Text(DemoLocalizations.of(context).cloudSynchronizing,style: TextStyle(color: Colors.white,fontSize: 12),), ], ), ); break; case SynFlag.failSynced: return Container( width: 60, height: 80, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( margin: EdgeInsets.only(bottom: 5), width: 20, height: 20, child: CircularProgressIndicator( value: 1.0, valueColor: AlwaysStoppedAnimation(Colors.white), ), ), Text(DemoLocalizations.of(context).synchronizeFailed,style: TextStyle(color: Colors.white),) ], ), ); break; case SynFlag.noNeedSynced: return Container(width: 60,height: 80,); break; default: } return Container(); } void uploadTask(TaskBean taskBean, String token) async{ if(synFlag == SynFlag.failSynced) return; ApiService.instance.postCreateTask( taskBean: taskBean, success: (UploadTaskBean bean){ syncedList.add(bean.uniqueId); taskBean.uniqueId = bean.uniqueId; taskBean.needUpdateToCloud = 'false'; if(syncedList.length == needSyncedLength){ DBProvider.db.updateTasks(needSynTasks).then((v){ setState(() { this.synFlag = SynFlag.cloudSynchronizing; print("更新完成"); ///将本地数据同步至云端后,从云端获取数据同步到本地(场景:手机A和手机B都在使用app) getCloudTasks(account, token); }); }); } setState(() {}); }, failed: (UploadTaskBean bean){ print("失败:${bean.toString()}"); setState(() { synFlag = SynFlag.failSynced; }); }, error: (msg){ print("错误:${msg}"); setState(() { synFlag = SynFlag.failSynced; }); }, token: token, cancelToken: cancelToken, ); } void checkIfNeedSyn(String account, String token) async{ final allTasks = await DBProvider.db.getAllTasks(account: account); List needSynTasks = []; int needSynNum = 0; for (var task in allTasks) { ///如果需要同步到云端 if(task.getNeedUpdateToCloud(task)){ needSynNum++; needSynTasks.add(task); } } ///当本地需要同步到云端的数据没有时,就从云端获取数据同步到本地 if(needSynNum == 0){ synFlag = SynFlag.noNeedSynced; getCloudTasks(account, token); return; } setState(() { needSyncedLength = needSynNum; this.needSynTasks.clear(); this.needSynTasks.addAll(needSynTasks); }); clickToSyn(); } void clickToSyn() async{ if(synFlag == SynFlag.synchronizing || synFlag == SynFlag.cloudSynchronizing) return; setState(() { synFlag = SynFlag.synchronizing; }); final token = await SharedUtil.instance.getString(Keys.token); for (var task in needSynTasks) { uploadTask(task, token); } } void getCloudTasks(String account, String token) async{ if(synFlag == SynFlag.failSynced) return; ApiService.instance.getTasks( params: { 'account':account, 'token':token }, success: (CloudTaskBean bean) async{ final tasks = bean.taskList; List needUpdateTasks = []; List needCreateTasks = []; for (var task in tasks) { final uniqueId = task.uniqueId; final localTask = await DBProvider.db.getTaskByUniqueId(uniqueId); ///如果本地没有查到这个task,就需要在本地重新创建 if(localTask == null){ needCreateTasks.add(task); } else { needUpdateTasks.add(localTask[0]); } } await DBProvider.db.updateTasks(needUpdateTasks); await DBProvider.db.createTasks(needCreateTasks); widget.mainPageModel.logic.getTasks().then((v){ widget.mainPageModel.needSyn = false; widget.mainPageModel.refresh(); }); setState(() { synFlag = SynFlag.noNeedSynced; }); }, failed: (UploadTaskBean bean){ setState(() { synFlag = SynFlag.failSynced; }); }, error: (msg){ setState(() { synFlag = SynFlag.failSynced; }); }, token: cancelToken, ); } ///这里有个隐藏问题,就是注册或者从登录页到主页来,登录操作相当于会执行两次,后续可以考虑解决这个问题 void doLogin() async{ final account = await SharedUtil.instance.getString(Keys.account) ?? 'default'; if(account == 'default'){ setState(() { synFlag = SynFlag.noNeedSynced; }); return; } final password = await SharedUtil.instance.getString(Keys.password); ApiService.instance.login( params: { "account": "${account}", "password": "${password}" }, success: (LoginBean loginBean) { loginSucceed = true; this.account = account; this.token = loginBean.token; SharedUtil.instance.saveString(Keys.account, account).then((value){ SharedUtil.instance.saveString(Keys.currentUserName, loginBean.username); SharedUtil.instance.saveString(Keys.token, loginBean.token); SharedUtil.instance.saveBoolean(Keys.hasLogged, true); if(loginBean.avatarUrl != null){ SharedUtil.instance.saveString(Keys.netAvatarPath, ApiStrategy.baseUrl + loginBean.avatarUrl); SharedUtil.instance.saveInt(Keys.currentAvatarType, CurrentAvatarType.net); widget.mainPageModel.currentAvatarUrl = ApiStrategy.baseUrl + loginBean.avatarUrl; widget.mainPageModel.currentAvatarType = CurrentAvatarType.net; widget.mainPageModel.logic.getCurrentAvatar(); } widget.mainPageModel.currentUserName = loginBean.username; ///检测是否需要进行本地与云端的数据同步 checkIfNeedSyn(account, loginBean.token); }); }, failed: (LoginBean loginBean) { setState(() { synFlag = SynFlag.failSynced; }); }, error: (msg) { setState(() { synFlag = SynFlag.failSynced; }); }, token: cancelToken, ); } } enum SynFlag{ ///没有将本地数据同步到云端 hasNotSynced, ///不需要进行同步 noNeedSynced, ///同步本地数据到云端中 synchronizing, ///同步云端数据到本地中 cloudSynchronizing, ///同步失败 failSynced, }