From 1482dd939239c741a87e8059e949f348e35feea5 Mon Sep 17 00:00:00 2001 From: dab246 Date: Fri, 1 Apr 2022 16:12:58 +0700 Subject: [PATCH] TF-358 Implement deferred for all pages --- lib/main/pages/app_pages.dart | 39 +++++++-------- lib/main/pages/deferred_widget.dart | 73 +++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 23 deletions(-) create mode 100644 lib/main/pages/deferred_widget.dart diff --git a/lib/main/pages/app_pages.dart b/lib/main/pages/app_pages.dart index 0f5c1474a..5d0bab1d8 100644 --- a/lib/main/pages/app_pages.dart +++ b/lib/main/pages/app_pages.dart @@ -1,22 +1,21 @@ +import 'package:get/get.dart'; import 'package:get/get_navigation/src/routes/get_route.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_bindings.dart'; -import 'package:tmail_ui_user/features/composer/presentation/composer_view.dart' - if (dart.library.html) 'package:tmail_ui_user/features/composer/presentation/composer_view_web.dart'; +import 'package:tmail_ui_user/features/composer/presentation/composer_view.dart' deferred as composer; import 'package:tmail_ui_user/features/destination_picker/presentation/destination_picker_bindings.dart'; -import 'package:tmail_ui_user/features/destination_picker/presentation/destination_picker_view.dart'; -import 'package:tmail_ui_user/features/email/presentation/email_view.dart'; +import 'package:tmail_ui_user/features/destination_picker/presentation/destination_picker_view.dart' deferred as destination_picker; +import 'package:tmail_ui_user/features/email/presentation/email_view.dart' deferred as email; import 'package:tmail_ui_user/features/home/presentation/home_bindings.dart'; import 'package:tmail_ui_user/features/home/presentation/home_view.dart'; import 'package:tmail_ui_user/features/login/presentation/login_bindings.dart'; -import 'package:tmail_ui_user/features/login/presentation/login_view.dart'; -import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_view.dart'; +import 'package:tmail_ui_user/features/login/presentation/login_view.dart' deferred as login; import 'package:tmail_ui_user/features/mailbox_creator/presentation/mailbox_creator_bindings.dart'; -import 'package:tmail_ui_user/features/mailbox_creator/presentation/mailbox_creator_view.dart'; +import 'package:tmail_ui_user/features/mailbox_creator/presentation/mailbox_creator_view.dart' deferred as mailbox_creator; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart' deferred as mailbox_dashBoard; import 'package:tmail_ui_user/features/session/presentation/session_page_bindings.dart'; -import 'package:tmail_ui_user/features/session/presentation/session_view.dart'; -import 'package:tmail_ui_user/features/thread/presentation/thread_view.dart'; +import 'package:tmail_ui_user/features/session/presentation/session_view.dart' deferred as session; +import 'package:tmail_ui_user/main/pages/deferred_widget.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart'; class AppPages { @@ -27,39 +26,33 @@ class AppPages { binding: HomeBindings()), GetPage( name: AppRoutes.LOGIN, - page: () => LoginView(), + page: () => DeferredWidget(login.loadLibrary, () => login.LoginView()), binding: LoginBindings()), GetPage( name: AppRoutes.SESSION, - page: () => SessionView(), + page: () => DeferredWidget(session.loadLibrary, () => session.SessionView()), binding: SessionPageBindings()), - GetPage( - name: AppRoutes.MAILBOX, - page: () => MailboxView()), GetPage( name: AppRoutes.MAILBOX_DASHBOARD, - page: () => MailboxDashBoardView(), + page: () => DeferredWidget(mailbox_dashBoard.loadLibrary, () => mailbox_dashBoard.MailboxDashBoardView()), binding: MailboxDashBoardBindings()), - GetPage( - name: AppRoutes.THREAD, - page: () => ThreadView()), GetPage( name: AppRoutes.EMAIL, - page: () => EmailView()), + page: () => DeferredWidget(email.loadLibrary, () => email.EmailView())), GetPage( name: AppRoutes.COMPOSER, opaque: false, - page: () => ComposerView(), + page: () => DeferredWidget(composer.loadLibrary, () => composer.ComposerView()), binding: ComposerBindings()), GetPage( name: AppRoutes.DESTINATION_PICKER, opaque: false, - page: () => DestinationPickerView(), + page: () => DeferredWidget(destination_picker.loadLibrary, () => destination_picker.DestinationPickerView()), binding: DestinationPickerBindings()), GetPage( name: AppRoutes.MAILBOX_CREATOR, opaque: false, - page: () => MailboxCreatorView(), + page: () => DeferredWidget(mailbox_creator.loadLibrary, () => mailbox_creator.MailboxCreatorView()), binding: MailboxCreatorBindings()), ]; } diff --git a/lib/main/pages/deferred_widget.dart b/lib/main/pages/deferred_widget.dart new file mode 100644 index 000000000..495faf235 --- /dev/null +++ b/lib/main/pages/deferred_widget.dart @@ -0,0 +1,73 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; + +typedef LibraryLoader = Future Function(); +typedef DeferredWidgetBuilder = Widget Function(); + +/// Wraps the child inside a deferred module loader. +/// +/// The child is created and a single instance of the Widget is maintained in +/// state as long as closure to create widget stays the same. +/// +class DeferredWidget extends StatefulWidget { + DeferredWidget( + this.libraryLoader, + this.createWidget, + {Key? key, Widget? placeholder} + ) : placeholder = placeholder ?? Container(), super(key: key); + + final LibraryLoader libraryLoader; + final DeferredWidgetBuilder createWidget; + final Widget placeholder; + static final Map> _moduleLoaders = {}; + static final Set _loadedModules = {}; + + static Future preload(LibraryLoader loader) { + if (!_moduleLoaders.containsKey(loader)) { + _moduleLoaders[loader] = loader().then((dynamic _) { + _loadedModules.add(loader); + }); + } + return _moduleLoaders[loader]!; + } + + @override + State createState() => _DeferredWidgetState(); +} + +class _DeferredWidgetState extends State { + _DeferredWidgetState(); + + Widget? _loadedChild; + DeferredWidgetBuilder? _loadedCreator; + + @override + void initState() { + /// If module was already loaded immediately create widget instead of + /// waiting for future or zone turn. + if (DeferredWidget._loadedModules.contains(widget.libraryLoader)) { + _onLibraryLoaded(); + } else { + DeferredWidget.preload(widget.libraryLoader).then((dynamic _) => _onLibraryLoaded()); + } + super.initState(); + } + + void _onLibraryLoaded() { + setState(() { + _loadedCreator = widget.createWidget; + _loadedChild = _loadedCreator!(); + }); + } + + @override + Widget build(BuildContext context) { + /// If closure to create widget changed, create new instance, otherwise + /// treat as const Widget. + if (_loadedCreator != widget.createWidget && _loadedCreator != null) { + _loadedCreator = widget.createWidget; + _loadedChild = _loadedCreator!(); + } + return _loadedChild ?? widget.placeholder; + } +} \ No newline at end of file