This repository has been archived by the owner on Jun 13, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
deferred_widget.dart
120 lines (106 loc) · 3.58 KB
/
deferred_widget.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Copyright 2019 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/material.dart';
typedef LibraryLoader = Future<void> 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, {
super.key,
Widget? placeholder,
}) : placeholder = placeholder ?? Container();
final LibraryLoader libraryLoader;
final DeferredWidgetBuilder createWidget;
final Widget placeholder;
static final Map<LibraryLoader, Future<void>> _moduleLoaders = {};
static final Set<LibraryLoader> _loadedModules = {};
static Future<void> preload(LibraryLoader loader) {
if (!_moduleLoaders.containsKey(loader)) {
_moduleLoaders[loader] = loader().then((dynamic _) {
_loadedModules.add(loader);
});
}
return _moduleLoaders[loader]!;
}
@override
State<DeferredWidget> createState() => _DeferredWidgetState();
}
class _DeferredWidgetState extends State<DeferredWidget> {
_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;
}
}
/// Displays a progress indicator and text description explaining that
/// the widget is a deferred component and is currently being installed.
class DeferredLoadingPlaceholder extends StatelessWidget {
const DeferredLoadingPlaceholder({
super.key,
this.name = 'This widget',
});
final String name;
@override
Widget build(BuildContext context) {
return Center(
child: Container(
decoration: BoxDecoration(
color: Colors.grey[700],
border: Border.all(
width: 20,
color: Colors.grey[700]!,
),
borderRadius: const BorderRadius.all(Radius.circular(10))),
width: 250,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('$name is installing.',
style: Theme.of(context).textTheme.headlineMedium),
Container(height: 10),
Text(
'$name is a deferred component which are downloaded and installed at runtime.',
style: Theme.of(context).textTheme.bodyLarge),
Container(height: 20),
const Center(child: CircularProgressIndicator()),
],
),
),
);
}
}