From 7391cb3e8bf9c800ce61e8732449b52220057e43 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 15 Nov 2021 19:34:40 +0200 Subject: [PATCH] Main page transitions improvements --- lib/core/context/tb_context.dart | 30 ++++- lib/core/context/tb_context_widget.dart | 1 + lib/main.dart | 22 ++-- lib/modules/dashboard/dashboard.dart | 2 +- lib/widgets/transition_indexed_stack.dart | 133 ---------------------- lib/widgets/two_page_view.dart | 110 ++++++++++++++++++ pubspec.lock | 7 ++ pubspec.yaml | 1 + 8 files changed, 155 insertions(+), 151 deletions(-) delete mode 100644 lib/widgets/transition_indexed_stack.dart create mode 100644 lib/widgets/two_page_view.dart diff --git a/lib/core/context/tb_context.dart b/lib/core/context/tb_context.dart index b371e42..40efc47 100644 --- a/lib/core/context/tb_context.dart +++ b/lib/core/context/tb_context.dart @@ -119,6 +119,7 @@ class TbContext { late final IosDeviceInfo? _iosInfo; late final String packageName; TbMainDashboardHolder? _mainDashboardHolder; + bool _closeMainFirst = false; GlobalKey messengerKey = GlobalKey(); late final ThingsboardClient tbClient; @@ -401,11 +402,8 @@ class TbContext { transition = TransitionType.native; } } - var res = await router.navigateTo(currentState!.context, path, transition: transition, transitionDuration: transitionDuration, replace: replace, clearStack: clearStack); - if (isOpenedDashboard) { - await _mainDashboardHolder?.closeMain(); - } - return res; + _closeMainFirst = isOpenedDashboard; + return await router.navigateTo(currentState!.context, path, transition: transition, transitionDuration: transitionDuration, replace: replace, clearStack: clearStack); } } @@ -422,7 +420,8 @@ class TbContext { )); } - void pop([T? result, BuildContext? context]) { + void pop([T? result, BuildContext? context]) async { + await closeMainIfNeeded(); var targetContext = context ?? currentState?.context; if (targetContext != null) { router.pop(targetContext, result); @@ -438,12 +437,25 @@ class TbContext { } Future willPop() async { + if (await closeMainIfNeeded()) { + return true; + } if (_mainDashboardHolder != null) { return await _mainDashboardHolder!.dashboardGoBack(); } return true; } + Future closeMainIfNeeded() async { + if (currentState != null) { + if (currentState!.closeMainFirst && _mainDashboardHolder != null) { + await _mainDashboardHolder!.closeMain(); + return true; + } + } + return false; + } + Future confirm({required String title, required String message, String cancel = 'Cancel', String ok = 'Ok'}) { return showDialog(context: currentState!.context, builder: (context) => AlertDialog( @@ -474,6 +486,12 @@ mixin HasTbContext { if (_tbContext.currentState != null) { ModalRoute.of(_tbContext.currentState!.context)?.addScopedWillPopCallback(_tbContext.willPop); } + if (_tbContext._closeMainFirst) { + _tbContext._closeMainFirst = false; + if (_tbContext.currentState != null) { + _tbContext.currentState!.closeMainFirst = true; + } + } } void setupTbContext(TbContextState currentState) { diff --git a/lib/core/context/tb_context_widget.dart b/lib/core/context/tb_context_widget.dart index 976d281..6294cf6 100644 --- a/lib/core/context/tb_context_widget.dart +++ b/lib/core/context/tb_context_widget.dart @@ -21,6 +21,7 @@ abstract class TbContextWidget extends StatefulWidget with HasTbContext { abstract class TbContextState extends State with HasTbContext { final bool handleLoading; + bool closeMainFirst = false; TbContextState({this.handleLoading = false}); diff --git a/lib/main.dart b/lib/main.dart index e5cf6a8..3cce13c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,7 +7,7 @@ import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:thingsboard_app/config/routes/router.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/modules/dashboard/main_dashboard_page.dart'; -import 'package:thingsboard_app/widgets/transition_indexed_stack.dart'; +import 'package:thingsboard_app/widgets/two_page_view.dart'; import 'config/themes/tb_theme.dart'; @@ -37,7 +37,7 @@ class ThingsboardApp extends StatefulWidget { class ThingsboardAppState extends State with TickerProviderStateMixin implements TbMainDashboardHolder { - final TransitionIndexedStackController _mainStackController = TransitionIndexedStackController(); + final TwoPageViewController _mainPageViewController = TwoPageViewController(); final MainDashboardPageController _mainDashboardPageController = MainDashboardPageController(); final GlobalKey mainAppKey = GlobalKey(); @@ -52,12 +52,12 @@ class ThingsboardAppState extends State with TickerProviderState @override Future navigateToDashboard(String dashboardId, {String? dashboardTitle, String? state, bool? hideToolbar, bool animate = true}) async { await _mainDashboardPageController.openDashboard(dashboardId, dashboardTitle: dashboardTitle, state: state, hideToolbar: hideToolbar); - await _openDashboard(animate: animate); + _openDashboard(animate: animate); } @override Future dashboardGoBack() async { - if (_mainStackController.index == 1) { + if (_mainPageViewController.index == 1) { var canGoBack = await _mainDashboardPageController.dashboardGoBack(); if (canGoBack) { closeDashboard(); @@ -88,11 +88,11 @@ class ThingsboardAppState extends State with TickerProviderState } bool isDashboardOpen() { - return _mainStackController.index == 1; + return _mainPageViewController.index == 1; } Future _openMain({bool animate: true}) async { - var res = await _mainStackController.open(0, animate: animate); + var res = await _mainPageViewController.open(0, animate: animate); if (res) { await _mainDashboardPageController.deactivateDashboard(); } @@ -103,18 +103,18 @@ class ThingsboardAppState extends State with TickerProviderState if (!isDashboardOpen()) { await _mainDashboardPageController.activateDashboard(); } - return _mainStackController.close(0, animate: animate); + return _mainPageViewController.close(0, animate: animate); } Future _openDashboard({bool animate: true}) async { if (!isDashboardOpen()) { _mainDashboardPageController.activateDashboard(); } - return _mainStackController.open(1, animate: animate); + return _mainPageViewController.open(1, animate: animate); } Future _closeDashboard({bool animate: true}) async { - var res = await _mainStackController.close(1, animate: animate); + var res = await _mainPageViewController.close(1, animate: animate); if (res) { _mainDashboardPageController.deactivateDashboard(); } @@ -132,8 +132,8 @@ class ThingsboardAppState extends State with TickerProviderState return MaterialApp( title: 'ThingsBoard', themeMode: ThemeMode.light, - home: TransitionIndexedStack( - controller: _mainStackController, + home: TwoPageView( + controller: _mainPageViewController, first: MaterialApp( key: mainAppKey, scaffoldMessengerKey: appRouter.tbContext.messengerKey, diff --git a/lib/modules/dashboard/dashboard.dart b/lib/modules/dashboard/dashboard.dart index 7014868..3c0bcaf 100644 --- a/lib/modules/dashboard/dashboard.dart +++ b/lib/modules/dashboard/dashboard.dart @@ -106,7 +106,7 @@ class _DashboardState extends TbContextState { mediaPlaybackRequiresUserGesture: false, javaScriptEnabled: true, cacheEnabled: true, - supportZoom: Platform.isIOS, + supportZoom: false, // useOnDownloadStart: true ), android: AndroidInAppWebViewOptions( diff --git a/lib/widgets/transition_indexed_stack.dart b/lib/widgets/transition_indexed_stack.dart deleted file mode 100644 index 59120a5..0000000 --- a/lib/widgets/transition_indexed_stack.dart +++ /dev/null @@ -1,133 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:thingsboard_app/utils/transition/page_transitions.dart'; - -class TransitionIndexedStackController { - - _TransitionIndexedStackState? _state; - - setTransitionIndexedStackState(_TransitionIndexedStackState state) { - _state = state; - } - - Future open(int index, {bool animate = true}) async { - if (_state != null) { - return _state!._open(index, animate: animate); - } - return false; - } - - Future close(int index, {bool animate = true}) async { - if (_state != null) { - return _state!._close(index, animate: animate); - } - return false; - } - - int? get index => _state?._selectedIndex; - -} - -class TransitionIndexedStack extends StatefulWidget { - final Widget first; - final Widget second; - final Duration duration; - final TransitionIndexedStackController? controller; - - const TransitionIndexedStack({ - Key? key, - required this.first, - required this.second, - this.controller, - this.duration = const Duration(milliseconds: 250) - }) : super(key: key); - - @override - _TransitionIndexedStackState createState() => _TransitionIndexedStackState(); - -} - -class _TransitionIndexedStackState extends State with TickerProviderStateMixin { - - late List _pages; - List _animationControllers = []; - int _selectedIndex = 0; - - @override - void initState() { - widget.controller?.setTransitionIndexedStackState(this); - final _duration = widget.duration; - _animationControllers = [ - AnimationController( - vsync: this, - duration: _duration, - ), - AnimationController( - vsync: this, - duration: _duration, - ) - ]; - _pages = [ - pageBuilder(UniqueKey(), widget.second, context, _animationControllers[1]), - pageBuilder(UniqueKey(), widget.first, context, _animationControllers[0]), - ]; - super.initState(); - } - - Future _open(int index, {bool animate = true}) async { - if (_selectedIndex != index) { - _selectedIndex = index; - setState(() { - _pages = _pages.reversed.toList(); - }); - if (animate) { - await _animationControllers[_selectedIndex].reverse(from: _animationControllers[_selectedIndex].upperBound); - } - return true; - } - return false; - } - - Future _close(int index, {bool animate = true}) async { - if (_selectedIndex == index) { - _selectedIndex = index == 1 ? 0 : 1; - if (animate) { - await _animationControllers[index].forward(from: _animationControllers[index].lowerBound); - } - setState(() { - _pages = _pages.reversed.toList(); - }); - if (animate) { - _animationControllers[index].value = _animationControllers[index].lowerBound; - } - return true; - } - return false; - } - - - - @override - void dispose() { - _animationControllers.forEach((controller) => controller.dispose()); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Stack( - children: _pages, - ); - } - - Widget pageBuilder(Key key, Widget widget, BuildContext context, Animation animation) { - return SlideTransition( - key: key, - position: Tween( - begin: Offset.zero, - end: const Offset(1, 0), - ).chain(CurveTween(curve: Curves.fastOutSlowIn)).animate(animation), - child: widget - ); - } - -} diff --git a/lib/widgets/two_page_view.dart b/lib/widgets/two_page_view.dart new file mode 100644 index 0000000..9845749 --- /dev/null +++ b/lib/widgets/two_page_view.dart @@ -0,0 +1,110 @@ +import 'package:flutter/widgets.dart'; +import 'package:preload_page_view/preload_page_view.dart'; + +class TwoPageViewController { + + _TwoPageViewState? _state; + + setTransitionIndexedStackState(_TwoPageViewState state) { + _state = state; + } + + Future open(int index, {bool animate = true}) async { + if (_state != null) { + return _state!._open(index, animate: animate); + } + return false; + } + + Future close(int index, {bool animate = true}) async { + if (_state != null) { + return _state!._close(index, animate: animate); + } + return false; + } + + int? get index => _state?._selectedIndex; + +} + +class TwoPageView extends StatefulWidget { + final Widget first; + final Widget second; + final Duration duration; + final TwoPageViewController? controller; + + const TwoPageView({ + Key? key, + required this.first, + required this.second, + this.controller, + this.duration = const Duration(milliseconds: 250) + }) : super(key: key); + + @override + _TwoPageViewState createState() => _TwoPageViewState(); + +} + +class _TwoPageViewState extends State { + + late List _pages; + bool _reverse = false; + int _selectedIndex = 0; + final PreloadPageController _pageController = PreloadPageController(); + + @override + void initState() { + widget.controller?.setTransitionIndexedStackState(this); + _pages = [widget.first, widget.second]; + super.initState(); + } + + Future _open(int index, {bool animate = true}) async { + if (_selectedIndex != index) { + _selectedIndex = index; + if (index == 0) { + setState(() { + _reverse = true; + }); + } + await _pageController.animateToPage(_selectedIndex, duration: widget.duration, curve: Curves.fastOutSlowIn); + return true; + } + return false; + } + + Future _close(int index, {bool animate = true}) async { + if (_selectedIndex == index) { + _selectedIndex = index == 1 ? 0 : 1; + await _pageController.animateToPage(_selectedIndex, duration: widget.duration, curve: Curves.fastOutSlowIn); + if (index == 0) { + setState(() { + _reverse = false; + }); + } + return true; + } + return false; + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return new PreloadPageView( + children: _pages, + physics: NeverScrollableScrollPhysics(), + reverse: _reverse, + onPageChanged: (int position) { + _selectedIndex = position; + }, + preloadPagesCount: 2, + controller: _pageController, + ); + } + +} diff --git a/pubspec.lock b/pubspec.lock index da09f3f..516d1ce 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -406,6 +406,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.2" + preload_page_view: + dependency: "direct main" + description: + name: preload_page_view + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.6" qr_code_scanner: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 9eee717..3ce9a7a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,6 +39,7 @@ dependencies: crypto: ^3.0.1 flutter_form_builder: ^6.0.1 universal_platform: ^1.0.0+1 + preload_page_view: ^0.1.6 dev_dependencies: flutter_test: