@@ -7,6 +7,7 @@ import 'package:flutter/scheduler.dart';
7
7
import '../generated/l10n/zulip_localizations.dart' ;
8
8
import '../log.dart' ;
9
9
import '../model/actions.dart' ;
10
+ import '../model/binding.dart' ;
10
11
import '../model/localizations.dart' ;
11
12
import '../model/store.dart' ;
12
13
import '../notifications/display.dart' ;
@@ -151,10 +152,13 @@ class ZulipApp extends StatefulWidget {
151
152
}
152
153
153
154
class _ZulipAppState extends State <ZulipApp > with WidgetsBindingObserver {
155
+ late final Future <GlobalStore > _globalStoreFuture;
156
+
154
157
@override
155
158
void initState () {
156
159
super .initState ();
157
160
WidgetsBinding .instance.addObserver (this );
161
+ _globalStoreFuture = ZulipBinding .instance.getGlobalStoreUniquely ();
158
162
}
159
163
160
164
@override
@@ -212,44 +216,101 @@ class _ZulipAppState extends State<ZulipApp> with WidgetsBindingObserver {
212
216
213
217
@override
214
218
Widget build (BuildContext context) {
215
- return GlobalStoreWidget (
216
- child: Builder (builder: (context) {
217
- return MaterialApp (
218
- onGenerateTitle: (BuildContext context) {
219
- return ZulipLocalizations .of (context).zulipAppTitle;
220
- },
221
- localizationsDelegates: ZulipLocalizations .localizationsDelegates,
222
- supportedLocales: ZulipLocalizations .supportedLocales,
223
- // The context has to be taken from the [Builder] because
224
- // [zulipThemeData] requires access to [GlobalStoreWidget] in the tree.
225
- theme: zulipThemeData (context),
226
-
227
- navigatorKey: ZulipApp .navigatorKey,
228
- navigatorObservers: [
229
- if (widget.navigatorObservers != null )
230
- ...widget.navigatorObservers! ,
231
- _PreventEmptyStack (),
232
- ],
233
- builder: (BuildContext context, Widget ? child) {
234
- if (! ZulipApp .ready.value) {
235
- SchedulerBinding .instance.addPostFrameCallback (
236
- (_) => widget._declareReady ());
237
- }
238
- GlobalLocalizations .zulipLocalizations = ZulipLocalizations .of (context);
239
- return child! ;
240
- },
219
+ return DeferredBuilderWidget (
220
+ future: _globalStoreFuture,
221
+ builder: (context, store) {
222
+ return GlobalStoreWidget (
223
+ store: store,
224
+ child: Builder (builder: (context) {
225
+ return MaterialApp (
226
+ onGenerateTitle: (BuildContext context) {
227
+ return ZulipLocalizations .of (context).zulipAppTitle;
228
+ },
229
+ localizationsDelegates: ZulipLocalizations .localizationsDelegates,
230
+ supportedLocales: ZulipLocalizations .supportedLocales,
231
+ // The context has to be taken from the [Builder] because
232
+ // [zulipThemeData] requires access to [GlobalStoreWidget] in the tree.
233
+ theme: zulipThemeData (context),
234
+
235
+ navigatorKey: ZulipApp .navigatorKey,
236
+ navigatorObservers: [
237
+ if (widget.navigatorObservers != null )
238
+ ...widget.navigatorObservers! ,
239
+ _PreventEmptyStack (),
240
+ ],
241
+ builder: (BuildContext context, Widget ? child) {
242
+ if (! ZulipApp .ready.value) {
243
+ SchedulerBinding .instance.addPostFrameCallback (
244
+ (_) => widget._declareReady ());
245
+ }
246
+ GlobalLocalizations .zulipLocalizations = ZulipLocalizations .of (context);
247
+ return child! ;
248
+ },
249
+
250
+ // We use onGenerateInitialRoutes for the real work of specifying the
251
+ // initial nav state. To do that we need [MaterialApp] to decide to
252
+ // build a [Navigator]... which means specifying either `home`, `routes`,
253
+ // `onGenerateRoute`, or `onUnknownRoute`. Make it `onGenerateRoute`.
254
+ // It never actually gets called, though: `onGenerateInitialRoutes`
255
+ // handles startup, and then we always push whole routes with methods
256
+ // like [Navigator.push], never mere names as with [Navigator.pushNamed].
257
+ onGenerateRoute: (_) => null ,
258
+
259
+ onGenerateInitialRoutes: _handleGenerateInitialRoutes);
260
+ }));
261
+ });
262
+ }
263
+ }
264
+
265
+ /// A widget that defers the builder until the provided [future] completes.
266
+ ///
267
+ /// It shows a placeholder widget while it waits for the [future]
268
+ /// to complete.
269
+ class DeferredBuilderWidget <T > extends StatefulWidget {
270
+ const DeferredBuilderWidget ({
271
+ super .key,
272
+ required this .future,
273
+ required this .builder,
274
+ this .placeholderBuilder = _defaultPlaceHolderBuilder,
275
+ });
276
+
277
+ final Future <T > future;
278
+
279
+ /// The widget to build when [future] completes, with it's result
280
+ /// passed as `result` .
281
+ final Widget Function (BuildContext context, T result) builder;
282
+
283
+ /// The placeholder widget to build while waiting for the [future]
284
+ /// to complete.
285
+ ///
286
+ /// By default, it will build the [LoadingPlaceholder] .
287
+ final Widget Function (BuildContext context) placeholderBuilder;
241
288
242
- // We use onGenerateInitialRoutes for the real work of specifying the
243
- // initial nav state. To do that we need [MaterialApp] to decide to
244
- // build a [Navigator]... which means specifying either `home`, `routes`,
245
- // `onGenerateRoute`, or `onUnknownRoute`. Make it `onGenerateRoute`.
246
- // It never actually gets called, though: `onGenerateInitialRoutes`
247
- // handles startup, and then we always push whole routes with methods
248
- // like [Navigator.push], never mere names as with [Navigator.pushNamed].
249
- onGenerateRoute: (_) => null ,
250
-
251
- onGenerateInitialRoutes: _handleGenerateInitialRoutes);
252
- }));
289
+ static Widget _defaultPlaceHolderBuilder (BuildContext context) {
290
+ return const LoadingPlaceholder ();
291
+ }
292
+
293
+ @override
294
+ State <DeferredBuilderWidget <T >> createState () => _DeferredBuilderWidgetState <T >();
295
+ }
296
+
297
+ class _DeferredBuilderWidgetState <T > extends State <DeferredBuilderWidget <T >> {
298
+ T ? _result;
299
+
300
+ @override
301
+ void initState () {
302
+ super .initState ();
303
+ () async {
304
+ _result = await widget.future;
305
+ if (mounted) setState (() {});
306
+ }();
307
+ }
308
+
309
+ @override
310
+ Widget build (BuildContext context) {
311
+ final result = _result;
312
+ if (result == null ) return widget.placeholderBuilder (context);
313
+ return widget.builder (context, result);
253
314
}
254
315
}
255
316
0 commit comments