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; } }