不使用GetX时如何在其他类中使用Flutter Context和MediaQuery

问题说明
不依赖GetX框架时,在非Widget类中调用BuildContext、使用MediaQuery能力的实现方式,以及解决The instance member 'context' can't be accessed in an initializer error报错。
实现方案
你碰到的初始化报错本质是:在类的属性初始化阶段就访问了尚未完成实例化的context对象,只要把context相关的调用挪到构造方法执行完成后的实例方法内、或者构造函数体内,就能规避这个错误。以下是三种可直接落地的实现方案:
方案1:显式传参(官方推荐,无副作用)
这是最稳妥的实现方式,不会有生命周期异常或者内存泄漏问题,直接在非Widget类的构造方法中声明接收BuildContext参数,调用时从Widget树传入即可。
// 自定义屏幕工具类示例 class ScreenUtils { final BuildContext context; ScreenUtils(this.context); double get screenW => MediaQuery.of(context).size.width; double get screenH => MediaQuery.of(context).size.height; double get statusBarH => MediaQuery.of(context).padding.top; } // 在Widget中使用 class DemoPage extends StatelessWidget { @override Widget build(BuildContext context) { // 传入当前节点的context final utils = ScreenUtils(context); print('屏幕宽度: ${utils.screenW}'); return const Scaffold(); } }
方案2:全局NavigatorKey(适合无上下文场景)
如果需要在网络回调、后台任务等完全拿不到传入context的位置调用,可以给MaterialApp配置全局导航Key,直接通过Key拿到当前的根上下文,不需要任何第三方依赖。
// 全局上下文工具类 class AppGlobal { static final GlobalKey<NavigatorState> navKey = GlobalKey<NavigatorState>(); static BuildContext? get currentCtx => navKey.currentContext; } // App入口配置key void main() { runApp(MaterialApp( navigatorKey: AppGlobal.navKey, home: const HomePage(), )); } // 任意类中直接调用 class RequestHelper { static void onResponseSuccess() { final ctx = AppGlobal.currentCtx; // 必须判断context是否挂载,避免生命周期异常 if (ctx != null && ctx.mounted) { final bottomPadding = MediaQuery.of(ctx).padding.bottom; } } }
注意:该方式拿到的是导航栈根节点的context,无法获取页面级Route的局部属性,涉及页面局部上下文的场景优先用传参方案。
方案3:页面级上下文缓存(适合绑定单页面的逻辑类)
如果是和单个页面绑定的逻辑类,可以在页面初始化时把context传入逻辑类缓存,页面销毁时清空引用即可,避免内存泄漏。
// 页面State class DemoPageState extends State<DemoPage> { late final PageLogic logic; @override void initState() { super.initState(); logic = PageLogic(context); } @override void dispose() { logic.clearRef(); super.dispose(); } @override Widget build(BuildContext context) { return const Scaffold(); } } // 页面对应的逻辑类 class PageLogic { BuildContext? _ctx; PageLogic(this._ctx); void calcSafeArea() { if (_ctx?.mounted ?? false) { final safeArea = MediaQuery.of(_ctx!).padding; } } void clearRef() { _ctx = null; } }
内容的提问来源于stack exchange,提问作者Fatih Yılmaz
相关产品推荐
相关产品推荐

