监听页面显示隐藏【Flutter笔记】
是这样的,父页面跳转子页面,子页面修改数据后,回退父页面需要更新,跳转子页面的组件隐藏的太深,不适合监听路由的返回做响应,之前是提升 provider 作用域实现的,跨页面更新数据,因为父页面是列表渲染,而且有多个类似的页面,就把 provider 封到另一个单独的组件中去了,出现的问题就是当前【页面数据更新时机不好掌控】。
解决办法:在flutter中 像路由、输入框(恰好挖个坑)等是有焦点【FocusNode】概念的,那可以利用 FocusNode+WidgetsBinding(里面有个帧回调) 实现页面的显示隐藏状态监听。
WidgetVisibilityStateMixin 类
获取当前顶层的焦点条件判断
import 'package:flutter/material.dart';
///页面状态 显示\隐藏
enum VisibilityState { hide, show }
mixin WidgetVisibilityStateMixin<T extends StatefulWidget> on State<T> implements WidgetsBindingObserver {
late FocusNode _ownFocusNode, _oldFocusNode, _newFocusNode;
VisibilityState visibilityState = VisibilityState.hide;
///忽略的焦点列表
List<FocusNode> _ignoreFocusList = [];
List<FocusNode> get ignoreFocusList => _ignoreFocusList;
set ignoreFocusList(List<FocusNode> list) => _ignoreFocusList = list;
///显示
void onShow() {
visibilityState = VisibilityState.show;
}
///不显示
void onHide() {
visibilityState = VisibilityState.hide;
}
_addFocusNodeChangeCb() {
_ownFocusNode = _oldFocusNode = _newFocusNode = FocusManager.instance.primaryFocus!;
WidgetsBinding.instance!.addObserver(this);
WidgetsBinding.instance!.addPersistentFrameCallback(focusNodeChangeCb);
onShow();
}
///焦点判断
void focusNodeChangeCb(_) {
_newFocusNode = FocusManager.instance.primaryFocus!;
if (_newFocusNode == _oldFocusNode) return;
_oldFocusNode = _newFocusNode;
if (_judgeNeedIgnore(_newFocusNode)) return;
if (_newFocusNode == _ownFocusNode) {
if (visibilityState != VisibilityState.show) {
onShow();
}
} else {
if (visibilityState != VisibilityState.hide) {
onHide();
}
}
}
///忽略焦点值
bool _judgeNeedIgnore(focusNode) {
return _ignoreFocusList.contains(focusNode);
}
@override
void initState() {
super.initState();
Future(_addFocusNodeChangeCb);
}
@override
void dispose() {
WidgetsBinding.instance!.removeObserver(this);
super.dispose();
}
}
简单使用
监听的页面需要实现 WidgetsBindingObserver
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
///监听的页面需要实现 WidgetsBindingObserver
class _HomePageState extends State<HomePage> with WidgetVisibilityStateMixin, WidgetsBindingObserver {
final inputFocus = FocusNode();
@override
onHide() {
super.onHide();
print('隐藏');
}
@override
void onShow() {
super.onShow();
print('显示');
}
@override
void initState() {
super.initState();
///添加需要忽略的TextField焦点
ignoreFocusList = [inputFocus];
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home"),
),
body: ListView(
children: [
ListTile(
title: Text("childPage"),
onTap: () {
Navigator.pushNamed(context, '/child');
},
),
///输入框需要单独处理焦点,及时释放,以免影响页面层级焦点的获取
TextField(
focusNode: inputFocus,
),
TextButton(
onPressed: () {
inputFocus.unfocus();
},
child: Text('释放输入框焦点测试'),
)
],
),
);
}
}
class ChildPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("childPage"),
),
body: Center(
child: Text(
"childPage",
style: Theme.of(context).textTheme.headline3,
),
),
);
}
}
完整例子
import 'package:flutter/material.dart';
enum VisibilityState { hide, show }
mixin WidgetVisibilityStateMixin<T extends StatefulWidget> on State<T> implements WidgetsBindingObserver {
late FocusNode _ownFocusNode, _oldFocusNode, _newFocusNode;
VisibilityState visibilityState = VisibilityState.hide;
///忽略的焦点列表
List<FocusNode> _ignoreFocusList = [];
List<FocusNode> get ignoreFocusList => _ignoreFocusList;
set ignoreFocusList(List<FocusNode> list) => _ignoreFocusList = list;
void onShow() {
visibilityState = VisibilityState.show;
}
void onHide() {
visibilityState = VisibilityState.hide;
}
_addFocusNodeChangeCb() {
_ownFocusNode = _oldFocusNode = _newFocusNode = FocusManager.instance.primaryFocus!;
WidgetsBinding.instance!.addObserver(this);
WidgetsBinding.instance!.addPersistentFrameCallback(focusNodeChangeCb);
onShow();
}
void focusNodeChangeCb(_) {
_newFocusNode = FocusManager.instance.primaryFocus!;
if (_newFocusNode == _oldFocusNode) return;
_oldFocusNode = _newFocusNode;
if (_judgeNeedIgnore(_newFocusNode)) return;
if (_newFocusNode == _ownFocusNode) {
///显示
if (visibilityState != VisibilityState.show) {
onShow();
}
} else {
///不显示
if (visibilityState != VisibilityState.hide) {
onHide();
}
}
}
bool _judgeNeedIgnore(focusNode) {
return _ignoreFocusList.contains(focusNode);
}
@override
void initState() {
super.initState();
Future(_addFocusNodeChangeCb);
}
@override
void dispose() {
WidgetsBinding.instance!.removeObserver(this);
super.dispose();
}
}
main() {
runApp(TestPageOnShowApp());
}
class TestPageOnShowApp extends StatelessWidget {
const TestPageOnShowApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
"/": (_) => HomePage(),
"/child": (_) => ChildPage(),
},
// home: HomePage(),
initialRoute: '/',
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with WidgetVisibilityStateMixin, WidgetsBindingObserver {
final inputFocus = FocusNode();
@override
onHide() {
super.onHide();
print('隐藏');
}
@override
void onShow() {
super.onShow();
print('显示');
}
@override
void initState() {
super.initState();
ignoreFocusList = [inputFocus];
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home"),
),
body: ListView(
children: [
ListTile(
title: Text("childPage"),
onTap: () {
Navigator.pushNamed(context, '/child');
},
),
///输入框需要单独处理焦点,及时释放,以免影响页面层级焦点的获取
TextField(
focusNode: inputFocus,
),
TextButton(
onPressed: () {
inputFocus.unfocus();
},
child: Text('释放焦点'),
)
],
),
);
}
}
class ChildPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("childPage"),
),
body: Center(
child: Text(
"childPage",
style: Theme.of(context).textTheme.headline3,
),
),
);
}
}