Flutter中PageBuilder内调用setState不生效问题如何解决?
问题根因
你在_TestPageState中调用setState只会触发当前页面的组件树重建,而通过PageRouteBuilder推出来的颜色选择器属于独立路由的组件树,默认不会跟随原页面的状态变更重新构建,因此修改actNum后只有原页面背景会更新,弹出的选择器页面会停留在初始状态。
解决方案
以下提供两种可行实现方案:
方案1:使用StatefulBuilder包裹弹出层内容
在PageRouteBuilder的pageBuilder中用StatefulBuilder包裹选择器内容,修改状态时同时触发弹出层自身的状态刷新,核心修改代码如下:
onTap: () { Navigator.of(context).push(PageRouteBuilder( opaque: false, maintainState: false, barrierDismissible: false, transitionDuration: Duration(milliseconds: 450), // 新增StatefulBuilder包裹页面内容 pageBuilder: (BuildContext context, _, __) => StatefulBuilder( builder: (context, setStateDialog) { return Container( height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, child: Stack( children: [ Positioned( bottom: 100.0, child: Hero( tag: "akcio", child: GestureDetector( onDoubleTap: () { Navigator.of(context).pop(); }, onTap: () { actNum = actNum == 1 ? 0 : 1; // 同时触发原页面和弹出层的状态刷新 setState(() {}); setStateDialog(() {}); }, child: Container( height: 100.0, width: MediaQuery.of(context).size.width, color: actNum == 1 ? Colors.greenAccent : Colors.white, child: Text('Change color!'), ), ), ), ) ], ), ); }, ) )); }
方案2:使用ValueNotifier做状态同步(更推荐)
将状态变量封装为ValueNotifier,两个页面同时监听状态变化,只要值更新两边会自动刷新UI,无需手动调用多个setState,完整修改代码如下:
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'dart:ui'; class TestPage extends StatefulWidget { @override _TestPageState createState() => _TestPageState(); } class CustomRectTween extends RectTween { final Rect? begin; final Rect? end; CustomRectTween({ required this.begin, required this.end, }) : super(begin: begin, end: end); @override Rect lerp(double t) { final elasticCurveValue = Curves.easeOut.transform(t); double? xLeft = begin!.left; double yLeft = end!.left; double xRight = begin!.right; double yRight = end!.right; double xTop = begin!.top; double yTop = end!.top; double xBottom = begin!.bottom; double yBottom = end!.bottom; return Rect.fromLTRB( lerpDouble(xLeft, yLeft, elasticCurveValue)!, lerpDouble(xTop, yTop, elasticCurveValue)!, lerpDouble(xRight, yRight, elasticCurveValue)!, lerpDouble(xBottom, yBottom, elasticCurveValue)!, ); } } class _TestPageState extends State<TestPage> { // 将actNum改为ValueNotifier final ValueNotifier<int> actNum = ValueNotifier(1); @override void initState() { super.initState(); } @override Widget build(BuildContext context) { return SafeArea( child: Scaffold( body: Stack( children: [ // 用ValueListenableBuilder监听状态变化 ValueListenableBuilder<int>( valueListenable: actNum, builder: (context, value, child) { return Container( color: value == 1 ? Colors.greenAccent : Colors.white, child: Hero( tag: "akcio", transitionOnUserGestures: true, createRectTween: (begin, end) { return CustomRectTween(begin: begin, end: end); }, child: GestureDetector( onTap: () { Navigator.of(context).push(PageRouteBuilder( opaque: false, maintainState: false, barrierDismissible: false, transitionDuration: Duration(milliseconds: 450), pageBuilder: (BuildContext context, _, __) { return Container( height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, child: Stack( children: [ Positioned( bottom: 100.0, child: Hero( tag: "akcio", child: GestureDetector( onDoubleTap: () { Navigator.of(context).pop(); }, onTap: () { // 直接修改ValueNotifier的值即可,无需调用setState actNum.value = actNum.value == 1 ? 0 : 1; }, // 弹出层也用ValueListenableBuilder监听状态 child: ValueListenableBuilder<int>( valueListenable: actNum, builder: (context, dialogValue, child) { return Container( height: 100.0, width: MediaQuery.of(context).size.width, color: dialogValue == 1 ? Colors.greenAccent : Colors.white, child: Text('Change color!'), ); }, ), ), ), ) ], ), ); } )); }, child: Icon( Icons.play_circle_fill, color: Colors.black, size: 75.0, ) ), ), ); } ) ], ) ), ); } }
内容的提问来源于stack exchange,提问作者Zoltan
相关产品推荐
相关产品推荐

