diff --git a/ios/Runner/GeneratedPluginRegistrant.m b/ios/Runner/GeneratedPluginRegistrant.m index 80ffbd7..b9af7ee 100644 --- a/ios/Runner/GeneratedPluginRegistrant.m +++ b/ios/Runner/GeneratedPluginRegistrant.m @@ -6,11 +6,13 @@ #import #import #import +#import #import #import #import #import #import +#import @implementation GeneratedPluginRegistrant @@ -18,11 +20,13 @@ [FlutterWebviewPlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterWebviewPlugin"]]; [ImageCropPlugin registerWithRegistrar:[registry registrarForPlugin:@"ImageCropPlugin"]]; [FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]]; + [OpenFilePlugin registerWithRegistrar:[registry registrarForPlugin:@"OpenFilePlugin"]]; [FLTPackageInfoPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTPackageInfoPlugin"]]; [FLTPathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTPathProviderPlugin"]]; [PermissionHandlerPlugin registerWithRegistrar:[registry registrarForPlugin:@"PermissionHandlerPlugin"]]; [FLTSharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTSharedPreferencesPlugin"]]; [SqflitePlugin registerWithRegistrar:[registry registrarForPlugin:@"SqflitePlugin"]]; + [FLTUrlLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTUrlLauncherPlugin"]]; } @end diff --git a/lib/i10n/localization_intl.dart b/lib/i10n/localization_intl.dart index 44f8f5e..1ddd0f0 100644 --- a/lib/i10n/localization_intl.dart +++ b/lib/i10n/localization_intl.dart @@ -171,6 +171,9 @@ class DemoLocalizations { desc: '检查更新', ); } + String get update => Intl.message('update', name: 'update', desc: '升级',); + String get newVersionIsComing => Intl.message('New version is comming!', name: 'newVersionIsComing', desc: '新版本来啦!',); + String get welcomeWord{ return Intl.message( diff --git a/lib/i10n/messages_en_US.dart b/lib/i10n/messages_en_US.dart index ad407a2..e599598 100644 --- a/lib/i10n/messages_en_US.dart +++ b/lib/i10n/messages_en_US.dart @@ -92,6 +92,7 @@ class MessageLookup extends MessageLookupByLibrary { "music" : MessageLookupByLibrary.simpleMessage("Music"), "navigatorSetting" : MessageLookupByLibrary.simpleMessage("Navigator Setting"), "netPicture" : MessageLookupByLibrary.simpleMessage("Network Picture"), + "newVersionIsComing" : MessageLookupByLibrary.simpleMessage("New version is comming!"), "ok" : MessageLookupByLibrary.simpleMessage("ok"), "openSystemSetting" : MessageLookupByLibrary.simpleMessage("Open System Setting"), "pickAColor" : MessageLookupByLibrary.simpleMessage("Pick a color!"), @@ -121,6 +122,7 @@ class MessageLookup extends MessageLookupByLibrary { "travel" : MessageLookupByLibrary.simpleMessage("Travel"), "tryToSearch" : MessageLookupByLibrary.simpleMessage("Try searching for the title or content"), "unknownDes" : MessageLookupByLibrary.simpleMessage("Unknown permission"), + "update" : MessageLookupByLibrary.simpleMessage("update"), "userNameCantBeNull" : MessageLookupByLibrary.simpleMessage("username can not be empty"), "versionDescription" : MessageLookupByLibrary.simpleMessage("Version Description"), "waitAMoment" : MessageLookupByLibrary.simpleMessage("please wait for a moment..."), diff --git a/lib/i10n/messages_zh_CN.dart b/lib/i10n/messages_zh_CN.dart index d8b4665..836bd5c 100644 --- a/lib/i10n/messages_zh_CN.dart +++ b/lib/i10n/messages_zh_CN.dart @@ -92,6 +92,7 @@ class MessageLookup extends MessageLookupByLibrary { "music" : MessageLookupByLibrary.simpleMessage("听歌"), "navigatorSetting" : MessageLookupByLibrary.simpleMessage("导航栏设置"), "netPicture" : MessageLookupByLibrary.simpleMessage("网络图片"), + "newVersionIsComing" : MessageLookupByLibrary.simpleMessage("新版本来啦!"), "ok" : MessageLookupByLibrary.simpleMessage("确定"), "openSystemSetting" : MessageLookupByLibrary.simpleMessage("打开系统设置"), "pickAColor" : MessageLookupByLibrary.simpleMessage("选择一个颜色吧!"), @@ -121,6 +122,7 @@ class MessageLookup extends MessageLookupByLibrary { "travel" : MessageLookupByLibrary.simpleMessage("旅行"), "tryToSearch" : MessageLookupByLibrary.simpleMessage("试试搜一下标题、内容吧"), "unknownDes" : MessageLookupByLibrary.simpleMessage("未知权限"), + "update" : MessageLookupByLibrary.simpleMessage("升级"), "userNameCantBeNull" : MessageLookupByLibrary.simpleMessage("昵称不能为空哦!"), "versionDescription" : MessageLookupByLibrary.simpleMessage("版本描述"), "waitAMoment" : MessageLookupByLibrary.simpleMessage("请稍后..."), diff --git a/lib/logic/edit_page_task_logic.dart b/lib/logic/edit_page_task_logic.dart index 18bad86..d6b1475 100644 --- a/lib/logic/edit_page_task_logic.dart +++ b/lib/logic/edit_page_task_logic.dart @@ -40,6 +40,7 @@ class EditTaskPageLogic { //提交一项任务 void submitOneItem() { String text = _model.textEditingController.text; + if(text.isEmpty) return; _model.taskDetails.add(TaskDetailBean(taskDetailName: text)); _model.textEditingController.clear(); _model.refresh(); diff --git a/lib/logic/main_page_logic.dart b/lib/logic/main_page_logic.dart index cbca507..b058de5 100644 --- a/lib/logic/main_page_logic.dart +++ b/lib/logic/main_page_logic.dart @@ -252,7 +252,7 @@ class MainPageLogic { _model.currentUserName = text; }, initialValue: _model.currentUserName, - onSure: () { + onPositive: () { if (_model.currentUserName.isEmpty) { showDialog( context: context, diff --git a/lib/pages/about_page.dart b/lib/pages/about_page.dart index 66cf523..37ba9ae 100644 --- a/lib/pages/about_page.dart +++ b/lib/pages/about_page.dart @@ -1,3 +1,4 @@ +import 'dart:io'; import 'dart:math'; import 'package:flutter/cupertino.dart'; @@ -7,6 +8,7 @@ import 'package:todo_list/i10n/localization_intl.dart'; import 'package:package_info/package_info.dart'; import 'package:todo_list/model/global_model.dart'; import 'package:todo_list/pages/webview_page.dart'; +import 'package:todo_list/widgets/update_dialog.dart'; class AboutPage extends StatefulWidget { @override @@ -109,7 +111,16 @@ class _AboutPageState extends State { ), ], ), - ) + ), + Platform.isAndroid ? Container( + child: IconButton(icon: Icon(Icons.cloud_upload,), onPressed: (){ + showDialog(context: context, builder: (ctx){ + return UpdateDialog( + + ); + }); + }), + ) : SizedBox(), ], ), Expanded( diff --git a/lib/pages/icon_setting_page.dart b/lib/pages/icon_setting_page.dart index 559ad26..83c5a70 100644 --- a/lib/pages/icon_setting_page.dart +++ b/lib/pages/icon_setting_page.dart @@ -3,6 +3,8 @@ import 'package:provider/provider.dart'; import 'package:todo_list/i10n/localization_intl.dart'; import 'package:todo_list/json/task_icon_bean.dart'; import 'package:todo_list/model/icon_setting_page_model.dart'; +import 'package:todo_list/widgets/custom_animated_icon.dart'; +import 'package:todo_list/widgets/custom_animated_switcher.dart'; class IconSettingPage extends StatelessWidget { @override @@ -28,18 +30,18 @@ class IconSettingPage extends StatelessWidget { ), Expanded( child: Container( - child: GestureDetector( - child: model.isDeleting - ? Icon( - Icons.check, - color: Colors.green, - size: 20, - ) - : Icon( - Icons.border_color, - size: 20, - ), - onTap: () { + child: CustomAnimatedSwitcher( + firstChild: Icon( + Icons.border_color, + size: 20, + ), + secondChild: Icon( + Icons.check, + size: 20, + color: Colors.greenAccent, + ), + hasChanged: model.isDeleting, + onTap: (){ model.isDeleting = !model.isDeleting; model.refresh(); }, @@ -72,8 +74,8 @@ class IconSettingPage extends StatelessWidget { InkWell( child: Icon( IconBean.fromBean(taskIcon.iconBean), - color: ColorBean.fromBean( - taskIcon.colorBean), + color: + ColorBean.fromBean(taskIcon.colorBean), size: 40, ), onTap: () { @@ -88,7 +90,9 @@ class IconSettingPage extends StatelessWidget { isEdit: true, ); }), - SizedBox(height: 2,), + SizedBox( + height: 2, + ), Text( taskIcon.taskName, overflow: TextOverflow.ellipsis, @@ -161,7 +165,9 @@ class IconSettingPage extends StatelessWidget { size: 30, ), ), - SizedBox(height: 8,), + SizedBox( + height: 8, + ), Expanded( child: Text( icons[index].iconName, diff --git a/lib/pages/nav_page.dart b/lib/pages/nav_page.dart index 3fa557d..b09f855 100644 --- a/lib/pages/nav_page.dart +++ b/lib/pages/nav_page.dart @@ -3,7 +3,6 @@ import 'dart:math'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:todo_list/config/custom_image_cache_manager.dart'; import 'package:todo_list/config/provider_config.dart'; import 'package:todo_list/i10n/localization_intl.dart'; import 'package:todo_list/json/weather_bean.dart'; @@ -103,13 +102,10 @@ class NavPage extends StatelessWidget { child: Container( height: netImageHeight, child: isDailyPic - ? FadeInImage( - image: NetworkImage(NavHeadType.DAILY_PIC_URL), - fit: BoxFit.cover, - placeholder: CachedNetworkImageProvider( - NavHeadType.DAILY_PIC_URL,cacheManager: CustomCacheManager(), - ), - ) + ? Image.network( + NavHeadType.DAILY_PIC_URL, + fit: BoxFit.cover, + ) : CachedNetworkImage( fit: BoxFit.cover, imageUrl: url, diff --git a/lib/pages/navigator_setting_page.dart b/lib/pages/navigator_setting_page.dart index cdfa810..3900cb5 100644 --- a/lib/pages/navigator_setting_page.dart +++ b/lib/pages/navigator_setting_page.dart @@ -2,7 +2,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:todo_list/config/custom_image_cache_manager.dart'; import 'package:todo_list/i10n/localization_intl.dart'; import 'package:todo_list/model/global_model.dart'; import 'package:todo_list/pages/photo_page.dart'; @@ -12,6 +11,7 @@ import 'package:todo_list/widgets/nav_head.dart'; class NavSettingPage extends StatelessWidget { @override Widget build(BuildContext context) { + final globalModel = Provider.of(context); return Scaffold( @@ -31,66 +31,51 @@ class NavSettingPage extends StatelessWidget { RadioListTile( value: NavHeadType.dailyPic, groupValue: globalModel.currentNavHeader, - subtitle: FadeInImage( - image: NetworkImage(NavHeadType.DAILY_PIC_URL), - fit: BoxFit.cover, - placeholder: CachedNetworkImageProvider( - NavHeadType.DAILY_PIC_URL, - cacheManager: CustomCacheManager()), - ), + subtitle: Image.network(NavHeadType.DAILY_PIC_URL), onChanged: (value) => onChanged(globalModel, value), title: Text(DemoLocalizations.of(context).dailyPic), ), RadioListTile( - value: NavHeadType.netPicture, - groupValue: globalModel.currentNavHeader, - onChanged: (value) => - onChanged(globalModel, value, context: context), - title: Text(DemoLocalizations.of(context).netPicture), - subtitle: globalModel.currentNetPicUrl == "" - ? null - : GestureDetector( - onTap: () { - Navigator.of(context) - .push(new CupertinoPageRoute(builder: (ctx) { - return PhotoPage( - selectValue: NavHeadType.netPicture, - ); - })); - }, - child: CachedNetworkImage( - imageUrl: globalModel.currentNetPicUrl, - placeholder: (context, url) => new Container( - alignment: Alignment.center, - child: CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation( - Theme.of(context).primaryColor), - ), - ), - errorWidget: (context, url, error) => new Icon( - Icons.error, - color: Colors.redAccent, - ), - ), - )), + value: NavHeadType.netPicture, + groupValue: globalModel.currentNavHeader, + onChanged: (value) => onChanged(globalModel, value,context: context), + title: Text(DemoLocalizations.of(context).netPicture), + subtitle: globalModel.currentNetPicUrl == "" ? null : GestureDetector( + onTap: (){ + Navigator.of(context).push(new CupertinoPageRoute(builder: (ctx) { + return PhotoPage( + selectValue: NavHeadType.netPicture, + ); + })); + }, + child: CachedNetworkImage( + imageUrl:globalModel.currentNetPicUrl, + placeholder: (context, url) => new Container( + alignment: Alignment.center, + child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Theme.of(context).primaryColor),), + ), + errorWidget: (context, url, error) => new Icon(Icons.error,color: Colors.redAccent,), + ), + ) + ), ], ), ), ); } - Future onChanged(GlobalModel globalModel, value, - {BuildContext context}) async { - if (context != null && globalModel.currentNetPicUrl == "") { + Future onChanged(GlobalModel globalModel, value, {BuildContext context}) async { + + if(context != null && globalModel.currentNetPicUrl == ""){ Navigator.of(context).push(new CupertinoPageRoute(builder: (ctx) { - return PhotoPage( - selectValue: value, - ); + return PhotoPage( + selectValue: value, + ); })); return; } - if (globalModel.currentNavHeader != value) { + if(globalModel.currentNavHeader != value){ globalModel.currentNavHeader = value; globalModel.refresh(); await SharedUtil.instance.saveString(Keys.currentNavHeader, value); @@ -98,10 +83,13 @@ class NavSettingPage extends StatelessWidget { } } -class NavHeadType { +class NavHeadType{ static const String meteorShower = "MeteorShower"; static const String dailyPic = "DailyPic"; static const String netPicture = "NetPicture"; - static const String DAILY_PIC_URL = "https://api.dujin.org/bing/1366.php"; + + static const String DAILY_PIC_URL = "https://api.dujin.org/bing/1366.php"; + + } diff --git a/lib/pages/setting_page.dart b/lib/pages/setting_page.dart index 641cb1a..264a738 100644 --- a/lib/pages/setting_page.dart +++ b/lib/pages/setting_page.dart @@ -154,8 +154,9 @@ class SettingPage extends StatelessWidget { if (value) { showDialog( context: context, - builder: (context) { + builder: (ctx1) { return EditDialog( + positiveWithPop: false, title: DemoLocalizations.of(context).enableWeatherShow, hintText: DemoLocalizations.of(context).inputCurrentCity, initialValue: globalModel.currentPosition, @@ -163,10 +164,12 @@ class SettingPage extends StatelessWidget { globalModel.currentPosition = text; }, sureTextStyle: TextStyle(color: globalModel.logic.getbwInDark()), - onSure: (){ + onPositive: (){ if(globalModel.currentPosition.isEmpty) return; CancelToken cancelToken = CancelToken(); - showDialog(context: context, builder: (ctx){ + showDialog(context: ctx1, builder: (ctx2){ + debugPrint("展示"); + return NetLoadingWidget( onRequest: (){ globalModel.logic.getWeatherNow(globalModel.currentPosition,controller: globalModel.loadingController); @@ -177,6 +180,7 @@ class SettingPage extends StatelessWidget { successText: DemoLocalizations.of(context).weatherSuccess, onSuccess: (){ Navigator.of(context).pop(); + Navigator.of(context).pop(); }, loadingController: globalModel.loadingController, ); diff --git a/lib/pages/theme_page.dart b/lib/pages/theme_page.dart index cd22c5d..8130fe3 100644 --- a/lib/pages/theme_page.dart +++ b/lib/pages/theme_page.dart @@ -8,6 +8,8 @@ import 'package:todo_list/utils/shared_util.dart'; import 'package:todo_list/utils/theme_util.dart'; import 'dart:convert'; +import 'package:todo_list/widgets/custom_animated_switcher.dart'; + class ThemePage extends StatelessWidget { @override Widget build(BuildContext context) { @@ -19,17 +21,28 @@ class ThemePage extends StatelessWidget { appBar: AppBar( title: Text(DemoLocalizations.of(context).changeTheme), actions: [ - IconButton( - icon: model.isDeleting - ? Icon(Icons.check) - : Icon( - Icons.border_color, - size: 18, - ), - onPressed: () { - model.isDeleting = !model.isDeleting; - model.refresh(); - }) + CustomAnimatedSwitcher( + firstChild: IconButton( + icon: Icon( + Icons.border_color, + size: 18, + color: globalModel.logic.getWhiteInDark(), + ), + onPressed: null, + ), + secondChild: IconButton( + icon: Icon( + Icons.check, + color: globalModel.logic.getWhiteInDark(), + ), + onPressed: null, + ), + hasChanged: model.isDeleting, + onTap: () { + model.isDeleting = !model.isDeleting; + model.refresh(); + }, + ), ], ), body: Container( @@ -113,7 +126,8 @@ class ThemePage extends StatelessWidget { onTap: () { globalModel.currentThemeBean = themeBean; globalModel.refresh(); - SharedUtil.instance.saveString(Keys.currentThemeBean, jsonEncode(themeBean.toMap())); + SharedUtil.instance + .saveString(Keys.currentThemeBean, jsonEncode(themeBean.toMap())); }, child: Container( height: (size.width - 140) / 3, diff --git a/lib/utils/icon_utils.dart b/lib/utils/icon_utils.dart index a292514..a7bc357 100644 --- a/lib/utils/icon_utils.dart +++ b/lib/utils/icon_utils.dart @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; + +//这个类是我通过文件操作的方式,以及对String做了各种操作,把系统的Icon信息提取出来 +//有一个本地的json文件也是这么生成的,有需要可以直接拿去使用,非常方便 class IconUtil { static IconUtil _instance; diff --git a/lib/widgets/custom_animated_icon.dart b/lib/widgets/custom_animated_icon.dart new file mode 100644 index 0000000..1245bd6 --- /dev/null +++ b/lib/widgets/custom_animated_icon.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; + +class CustomAnimatedIcon extends StatefulWidget { + final Color color; + final double size; + final AnimatedIconData firstIcon; + final AnimatedIconData secondIcon; + final Duration duration; + final VoidCallback onTap; + final bool hasTapped; + + const CustomAnimatedIcon( + {Key key, + this.color, + this.size, + @required this.firstIcon, + @required this.secondIcon, + this.duration, + this.onTap, this.hasTapped = false}) + : super(key: key); + + @override + _CustomAnimatedIconState createState() => _CustomAnimatedIconState(); +} + +class _CustomAnimatedIconState extends State + with SingleTickerProviderStateMixin { + AnimationController _controller; + Animation _animation; + bool hasTapped; + + @override + void initState() { + _controller = AnimationController( + vsync: this, duration: widget.duration ?? Duration(milliseconds: 300)); + _animation = Tween(begin: 0.0, end: 1.0).animate(_controller); + hasTapped = widget.hasTapped; + super.initState(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final IconThemeData iconTheme = IconTheme.of(context); + + + return InkWell( + child: AnimatedIcon( + icon: hasTapped ? widget.secondIcon : widget.firstIcon, + progress: _animation, + color: widget.color ?? iconTheme, + size: widget.size ?? iconTheme.size, + ), + onTap: () { + if (widget.onTap != null) { + widget.onTap(); + } + if(mounted){ + setState(() { + hasTapped = !hasTapped; + }); + } + }, + ); + } +} diff --git a/lib/widgets/custom_animated_switcher.dart b/lib/widgets/custom_animated_switcher.dart new file mode 100644 index 0000000..545e3d7 --- /dev/null +++ b/lib/widgets/custom_animated_switcher.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; + +class CustomAnimatedSwitcher extends StatefulWidget { + final Duration duration; + final Widget firstChild; + final Widget secondChild; + final bool hasChanged; + final VoidCallback onTap; + + const CustomAnimatedSwitcher({ + Key key, + this.duration, + @required this.firstChild, + @required this.secondChild, + this.hasChanged = false, this.onTap, + }) : super(key: key); + + @override + _CustomAnimatedSwitcherState createState() => _CustomAnimatedSwitcherState(); +} + +class _CustomAnimatedSwitcherState extends State { + + bool hasChanged; + Widget theChild; + + @override + void initState() { + hasChanged = widget.hasChanged; + theChild = Container(key: ValueKey(0),child: widget.firstChild,); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: (){ + if(widget.onTap != null){ + widget.onTap(); + } + setState(() { + hasChanged = !hasChanged; + if(hasChanged){ + theChild = Container(key: ValueKey(1),child: widget.secondChild,); + } else{ + theChild = Container(key: ValueKey(0),child: widget.firstChild,); + } + }); + }, + child: AnimatedSwitcher( + duration: widget.duration ?? const Duration(milliseconds : 300), + child: theChild, + transitionBuilder: (child,animation){ + return ScaleTransition(scale: animation, child: child,); + }, + ), + ); + } +} diff --git a/lib/widgets/custom_icon_widget.dart b/lib/widgets/custom_icon_widget.dart index 75e477b..74026f9 100644 --- a/lib/widgets/custom_icon_widget.dart +++ b/lib/widgets/custom_icon_widget.dart @@ -116,7 +116,6 @@ class _CustomIconWidgetState extends State { width: 35, margin: EdgeInsets.all(10), decoration: BoxDecoration( - shape: BoxShape.rectangle, gradient: LinearGradient( colors: [ Colors.redAccent, @@ -129,6 +128,7 @@ class _CustomIconWidgetState extends State { border: currentSelectIndex == 7 ? Border.all(color: currentIconColor, width: 4) : null, + borderRadius: BorderRadius.all(Radius.circular(10)), ), ), ), diff --git a/lib/widgets/edit_dialog.dart b/lib/widgets/edit_dialog.dart index 440afeb..f2b42f5 100644 --- a/lib/widgets/edit_dialog.dart +++ b/lib/widgets/edit_dialog.dart @@ -2,7 +2,8 @@ import 'package:flutter/material.dart'; import 'package:todo_list/i10n/localization_intl.dart'; class EditDialog extends StatelessWidget { - final VoidCallback onSure; + final VoidCallback onPositive; + final bool positiveWithPop; final String title; final String hintText; final String initialValue; @@ -12,10 +13,14 @@ class EditDialog extends StatelessWidget { const EditDialog({ Key key, - this.onSure, + this.onPositive, this.title, this.hintText, - this.initialValue, this.onValueChanged, this.cancelTextStyle, this.sureTextStyle, + this.initialValue, + this.onValueChanged, + this.cancelTextStyle, + this.sureTextStyle, + this.positiveWithPop = true, }) : super(key: key); @override @@ -28,9 +33,8 @@ class EditDialog extends StatelessWidget { autovalidate: true, child: TextFormField( initialValue: initialValue ?? "", - validator: (text){ - if(onValueChanged != null) - onValueChanged(text); + validator: (text) { + if (onValueChanged != null) onValueChanged(text); }, decoration: InputDecoration( hintText: hintText ?? "", @@ -44,17 +48,17 @@ class EditDialog extends StatelessWidget { }, child: Text( DemoLocalizations.of(context).cancel, - style:cancelTextStyle ?? TextStyle(color: Colors.redAccent), + style: cancelTextStyle ?? TextStyle(color: Colors.redAccent), ), ), FlatButton( - onPressed: (){ - if(onSure != null) onSure(); - Navigator.pop(context); - + onPressed: () { + if (onPositive != null) onPositive(); + if(positiveWithPop) Navigator.of(context).pop(); + print("确定"); }, child: Text(DemoLocalizations.of(context).ok, - style:sureTextStyle ?? TextStyle(color: Colors.black)), + style: sureTextStyle ?? TextStyle(color: Colors.black)), ), ], ); diff --git a/lib/widgets/update_dialog.dart b/lib/widgets/update_dialog.dart new file mode 100644 index 0000000..5d9a209 --- /dev/null +++ b/lib/widgets/update_dialog.dart @@ -0,0 +1,199 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:todo_list/config/api_strategy.dart'; +import 'package:todo_list/i10n/localization_intl.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:open_file/open_file.dart'; + +class UpdateDialog extends StatefulWidget { + final String version; + final String updateInfo; + final String updateUrl; + final bool isForce; + final Color backgroundColor; + + UpdateDialog({ + this.version = "1.0.0", + this.updateInfo = "", + this.updateUrl = "", + this.isForce = false, this.backgroundColor, + }); + + @override + State createState() => new UpdateDialogState(); +} + +class UpdateDialogState extends State { + int _downloadProgress = 0; + CancelToken token; + + @override + Widget build(BuildContext context) { + final bgColor = Theme + .of(context) + .primaryColor; + final size = MediaQuery + .of(context) + .size; + final isVertical = size.height > size.width; + final marginLeft = isVertical ? size.width / 8 : size.width / 4; + final marginTop = isVertical ? size.height / 4 : size.height / 8; + + + return WillPopScope( + onWillPop: () async => false, + child: Container( + margin: EdgeInsets.fromLTRB( + marginLeft, marginTop, marginLeft, marginTop), + decoration: BoxDecoration( + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(10), + color: bgColor, + ), + child: Column( + children: [ + Expanded( + flex: 2, + child: Container( + margin: EdgeInsets.fromLTRB(5, 30, 5, 5), + child: Material( + child: Text( + DemoLocalizations + .of(context) + .newVersionIsComing, + style: TextStyle(color: Colors.white, fontSize: 20), + ), + color: Colors.transparent, + )), + ), + Expanded( + flex: 5, + child: Container( + margin: EdgeInsets.all(5), + alignment: Alignment.center, + child: Material( + color: Colors.transparent, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Text( + widget.updateInfo ?? "", + style: TextStyle(color: Colors.white), + ), + ), + ))), + _downloadProgress != 0 + ? Expanded( + child: Container( + child: LinearProgressIndicator( + valueColor: + new AlwaysStoppedAnimation(Colors.orange), + backgroundColor: Colors.grey[300], + value: _downloadProgress / 100, //精确模式,进度20% + ), + ), + flex: 1, + ) + : SizedBox(), + Expanded( + flex: 2, + child: Container( + decoration: BoxDecoration( + shape: BoxShape.rectangle, + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(10), + bottomRight: Radius.circular(10)), + color: Colors.white, + ), + child: Row( + children: [ + !widget.isForce + ? Expanded( + flex: 1, + child: FlatButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text( + DemoLocalizations + .of(context) + .cancel, + style: TextStyle(color: Colors.grey, fontSize: 16), + )), + ) + : SizedBox(), + !widget.isForce + ? Container( + width: 1, + color: Colors.grey[100], + ) + : SizedBox(), + Expanded( + flex: 1, + child: FlatButton( + onPressed: () async { + if (Platform.isAndroid) { + _androidUpdate(); + } else if (Platform.isIOS) { + _iosUpdate(); + } + }, + child: Text( + DemoLocalizations + .of(context) + .update, + style: TextStyle(color: Colors.black, fontSize: 16), + )), + ), + ], + ), + ), + ) + ], + ), + ), + ); + } + + void _androidUpdate() async { + String path = (await getExternalStorageDirectory()).path; + debugPrint("获取的目录:${path}"); + ApiStrategy + .getInstance() + .client + .download(widget.updateUrl, path + "/Download/" + "release.apk", + cancelToken: token, onReceiveProgress: (int count, int total) { + setState(() { + _downloadProgress = ((count / total) * 100).toInt(); + if (_downloadProgress == 100) { + debugPrint("读取的目录:${path}"); + try { + OpenFile.open(path + "/Download/" + "release.apk"); + } catch (e) {} + Navigator.of(context).pop(); + } + }); + }); + } + + void _iosUpdate() { + launch(widget.updateUrl); + } + + + @override + void initState() { + super.initState(); + token = new CancelToken(); + } + + @override + void dispose() { + super.dispose(); + token?.cancel(); + debugPrint("升级销毁"); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index e10ca11..b9f5d7d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -72,6 +72,11 @@ dev_dependencies: pull_to_refresh: ^1.5.0 #看图片 photo_view: ^0.3.3 + #url + url_launcher: ^5.1.0 + #打开文件,android更新下载安装包用 + open_file: ^2.0.3 + # For information on the generic Dart part of this file, see the diff --git a/res/intl_en_US.arb b/res/intl_en_US.arb index ac0b9de..3fb847f 100644 --- a/res/intl_en_US.arb +++ b/res/intl_en_US.arb @@ -428,6 +428,18 @@ "type": "text", "placeholders": {} }, + "update": "update", + "@update": { + "description": "升级", + "type": "text", + "placeholders": {} + }, + "newVersionIsComing": "New version is comming!", + "@newVersionIsComing": { + "description": "新版本来啦!", + "type": "text", + "placeholders": {} + }, "writeAtLeastOneTaskItem": "Please write at least one task.", "@writeAtLeastOneTaskItem": { "description": "请至少写下一项任务哦", diff --git a/res/intl_messages.arb b/res/intl_messages.arb index c71c345..8bddd3d 100644 --- a/res/intl_messages.arb +++ b/res/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2019-07-26T16:49:38.161457", + "@@last_modified": "2019-07-26T18:12:05.019730", "appName": "One Day List", "@appName": { "description": "app的名字", @@ -434,6 +434,18 @@ "type": "text", "placeholders": {} }, + "update": "update", + "@update": { + "description": "升级", + "type": "text", + "placeholders": {} + }, + "newVersionIsComing": "New version is comming!", + "@newVersionIsComing": { + "description": "新版本来啦!", + "type": "text", + "placeholders": {} + }, "welcomeWord": "Hello! ", "@welcomeWord": { "description": "主页的欢迎词", diff --git a/res/intl_zh_CN.arb b/res/intl_zh_CN.arb index 95a26ee..076d7d1 100644 --- a/res/intl_zh_CN.arb +++ b/res/intl_zh_CN.arb @@ -410,6 +410,18 @@ "type": "text", "placeholders": {} }, + "update": "升级", + "@update": { + "description": "升级", + "type": "text", + "placeholders": {} + }, + "newVersionIsComing": "新版本来啦!", + "@newVersionIsComing": { + "description": "新版本来啦!", + "type": "text", + "placeholders": {} + }, "bgChangeWithCard": "背景跟随任务图标颜色", "@bgChangeWithCard": { "description": "背景跟随任务图标颜色",