Created
May 4, 2025 18:17
-
-
Save fredgrott/294bf9d2d5f967abb2c9c4eb55cd564b to your computer and use it in GitHub Desktop.
app lifecycle mixin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2025 Fredrick Allan Grott. All rights reserved. | |
// Use of this source code is governed by a BSD-style | |
// license that can be found in the LICENSE file. | |
import 'package:flutter/material.dart'; | |
import 'package:lifecycle/lifecycle.dart'; | |
import 'package:userinterface/core/lifecycle/app_lifecycle_globals.dart'; | |
/// Subscribes lifecycle events for normal widgets. | |
/// This is used to wrap the scaffold of the app so that | |
/// we have non-null appView and appDisplay globals. | |
/// | |
/// One still puts the defaultLifecycleObserver from the | |
/// Lifecycle package in the navigationObservers slot | |
/// of the MaterialApp.route constructor. | |
mixin AppLifecycleMixin<T extends StatefulWidget> on State<T>, LifecycleAware { | |
LifecycleObserver? _lifecycleObserver; | |
WidgetDispatchLifecycleMixin? _widgetDispatchLifecycleMixin; | |
@override | |
void initState() { | |
super.initState(); | |
// Dispatches [LifecycleEvent.push] event. | |
handleLifecycleEvents([LifecycleEvent.push]); | |
} | |
@override | |
void didChangeDependencies() { | |
super.didChangeDependencies(); | |
final ModalRoute? route = ModalRoute.of(context); | |
// Avoids re-subscribe when a route is popping. | |
// When called Navigator#pop(), the [_RouteLifecycle] will change to [popping], | |
// then notify the [NavigatorObserver]. | |
// | |
// 如果当前route正在popping,避免重复订阅。 | |
if (route == null || !route.isActive) return; | |
// grab appView | |
appView = View.maybeOf(context); | |
// 重新查找 [WidgetDispatchLifecycleMixin]。 | |
_widgetDispatchLifecycleMixin = null; | |
context.visitAncestorElements((element) { | |
if (element is StatefulElement && element.state is WidgetDispatchLifecycleMixin) { | |
_widgetDispatchLifecycleMixin = element.state as WidgetDispatchLifecycleMixin; | |
return false; | |
} | |
return true; | |
}); | |
// 优先从 [WidgetDispatchLifecycleMixin] 中订阅。 | |
if (_widgetDispatchLifecycleMixin != null) { | |
_widgetDispatchLifecycleMixin!.subscribe(this); | |
} else { | |
_lifecycleObserver = LifecycleObserver.internalGet(context); | |
_lifecycleObserver!.subscribe(this, route); | |
} | |
} | |
// To prevent letter-boxing on fodlables, see | |
// https://docs.flutter.dev/ui/adaptive-responsive/large-screens | |
void didChangeMetrics() { | |
// so now we can use appDisplay to get devicePixelRatio and size | |
// without using MediaQueryData.fromView( FlutterView view) | |
// This will prevent letter-boxing with foldables. | |
appDisplay = appView?.display; | |
} | |
@override | |
void dispose() { | |
// Dispatches [LifecycleEvent.pop]. | |
handleLifecycleEvents([LifecycleEvent.pop]); | |
_lifecycleObserver?.unsubscribe(this); | |
_widgetDispatchLifecycleMixin?.unsubscribe(this); | |
super.dispose(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment