Flutter Web开发遇Go Router空值错误,请求技术排查
Flutter Web 空值错误定位与解决
错误信息
Builder:file:///Users/mertyavuz/.pub-cache/hosted/pub.dev/go_router-14.1.4/lib/src/builder.dart:256:9
相关代码
路由配置
import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hotor_web/view/authentication/login/login_screen.dart'; import 'package:hotor_web/view/authentication/register/register_screen.dart'; import '../../view/management/screen_management.dart'; class AppRouterConfig { late final GoRouter router = GoRouter( routes: <RouteBase>[ GoRoute( path: '/', builder: (BuildContext context, GoRouterState state) { return const ScreenManagement(); }, routes: <RouteBase>[ GoRoute( path: 'login', builder: (BuildContext context, GoRouterState state) { return const LoginScreen(); }, ), GoRoute( path: 'register', builder: (BuildContext context, GoRouterState state) { return const RegisterScreen(); }, ), GoRoute( path: 'admin', builder: (BuildContext context, GoRouterState state) { return const LoginScreen(); }, ), GoRoute( path: 'openlearnings', builder: (BuildContext context, GoRouterState state) { return const ScreenManagement(initialPage: 2); }, ), GoRoute( path: 'activelearnings', builder: (BuildContext context, GoRouterState state) { return const ScreenManagement(initialPage: 1); }, ), GoRoute( path: 'help', builder: (BuildContext context, GoRouterState state) { return const ScreenManagement(initialPage: 3); }, ), GoRoute( path: 'settings', builder: (BuildContext context, GoRouterState state) { return const ScreenManagement(initialPage: 4); }, ), ], ), ], ); }
注册页面
import 'package:flutter/material.dart'; import 'package:hotor_web/core/base/view/base_view.dart'; import 'package:hotor_web/core/constants/images.dart'; import 'package:hotor_web/view/authentication/register/pages/register_screen_one.dart'; import 'package:hotor_web/view/authentication/register/view_model/register_view_model.dart'; import '../../../core/base/state/base_state.dart'; import '../../../core/services/register/register_service.dart'; import 'pages/register_screen_three.dart'; import 'pages/register_screen_two.dart'; class RegisterScreen extends StatefulWidget { const RegisterScreen({super.key, this.initialPage = 0}); final int initialPage; @override State<RegisterScreen> createState() => _RegisterScreenState(); } class _RegisterScreenState extends BaseState<RegisterScreen> { final RegisterService registerService = RegisterService(); final RegisterViewModel _viewModel = RegisterViewModel(); @override void initState() { super.initState(); _viewModel.pageController = PageController(initialPage: widget.initialPage); print("PageController initialized with initialPage: ${widget.initialPage}"); } @override Widget build(BuildContext context) { print("Building RegisterScreen with context: $context"); return BaseView( viewModel: _viewModel, onPageBuilder: (context, dynamic viewModel) { print("Inside onPageBuilder with viewModel: $viewModel"); return scaffoldBody(); }, ); } Scaffold scaffoldBody() { print("Building scaffoldBody"); return Scaffold( body: SizedBox( width: screenWidth(), child: Row( children: [ Expanded( flex: 5, child: Image.asset( AppImages.loginBackground.imagePath, fit: BoxFit.cover, ), ), Expanded( flex: 5, child: PageView( controller: _viewModel.pageController, physics: const NeverScrollableScrollPhysics(), children: const [ RegisterScreenOne(), RegisterScreenTwo(), RegisterScreenThree(), ], ), ), ], ), ), ); } }
注册第一步页面
import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:hotor_web/core/components/form_widgets/management/validate_management.dart'; import '../../../../core/base/state/base_state.dart'; import '../../../../core/components/form_widgets/dropdown_form_widget.dart'; import '../../../../core/components/form_widgets/text_form_widget.dart'; import '../../../widgets/step_progress_widget.dart'; class RegisterScreenOne extends StatefulWidget { const RegisterScreenOne({super.key}); @override State<RegisterScreenOne> createState() => _RegisterScreenOneState(); } class _RegisterScreenOneState extends BaseState<RegisterScreenOne> { @override Widget build(BuildContext context) { print("dynamicHeight: ${dynamicHeight(0.1)}"); print("dynamicWidth: ${dynamicWidth(0.7)}"); print("currentTheme: $currentTheme"); return Padding( padding: EdgeInsets.symmetric( vertical: dynamicHeight(0.1), ), child: Column( children: [ progress(), SizedBox( height: dynamicHeight(0.05), ), FormBuilder( child: SizedBox( width: dynamicWidth(0.7), child: const Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ //* Name TextFormWidget( hintText: "Ad", title: "Ad", keyboardType: TextInputType.name, identifierName: "name", validationType: InputValidationType.text, width: 250, labelVisibility: true, ), SizedBox(width: 20), TextFormWidget( hintText: "Soyad", title: "Soyad", keyboardType: TextInputType.name, identifierName: "surname", validationType: InputValidationType.text, width: 250, labelVisibility: true, ), ], ), const TextFormWidget( hintText: "Eğer yok ise 'Yok' yazabilirsiniz.", title: "E-posta", keyboardType: TextInputType.name, identifierName: "email", validationType: InputValidationType.text, width: 520, labelVisibility: true, ), DropDownFormWidget( identifierName: "jobTag", hintText: "İş Tanımı Seçiniz", title: "İş Tanımı", items: ["e", "s"], // ViewModel'den gelen liste labelVisible: true, ), ], ), ), ), ], ), ); } Padding progress() { return Padding( padding: EdgeInsets.symmetric( horizontal: dynamicWidth(0.1), ), child: Column( children: [ const StepProgressWidget( currentPage: 1, totalSteps: 3, ), SizedBox( height: dynamicHeight(0.02), ), //* Title Text( "Bilgilerinizi girerek kayıt olma işlemine başlayabilirsiniz.", style: currentTheme.textTheme.bodyLarge, textAlign: TextAlign.center, ), ], ), ); } }
问题定位
- GoRouter上下文传递异常:错误触发点在go_router的builder方法,说明路由跳转时无法正确构建目标页面,大概率是页面依赖的某个实例为空。
- BaseState方法空值风险:
RegisterScreenOne中调用的dynamicHeight、dynamicWidth、currentTheme依赖BaseState的上下文,如果BaseState未正确绑定context,会直接抛出空值错误。 - PageController初始化问题:
_viewModel.pageController在initState中初始化,但如果BaseView未正确持有_viewModel,会导致PageController为空,PageView无法正常渲染。 - FormBuilder缺失key:未设置FormBuilder的key,可能导致表单状态管理异常,间接触发空值。
解决方案
1. 修复BaseState上下文绑定
确保BaseState的context已正确初始化,避免在build方法中过早调用依赖上下文的方法。可以在调用dynamicHeight等方法前添加空值判断:
// 在_RegisterScreenOneState中修改 @override Widget build(BuildContext context) { if (context == null) return const SizedBox.shrink(); // 后续代码 }
2. 确保PageController非空
将RegisterViewModel中的pageController改为late初始化,并在didChangeDependencies中确保初始化完成:
// RegisterViewModel类 class RegisterViewModel { late PageController pageController; } // _RegisterScreenState中修改 @override void didChangeDependencies() { super.didChangeDependencies(); if (!_viewModel.pageController.hasClients) { _viewModel.pageController = PageController(initialPage: widget.initialPage); } } // scaffoldBody中添加空值判断 PageView( controller: _viewModel.pageController ?? PageController(initialPage: 0), // 其他属性 )
3. 为FormBuilder添加全局key
class _RegisterScreenOneState extends BaseState<RegisterScreenOne> { final _formKey = GlobalKey<FormBuilderState>(); @override Widget build(BuildContext context) { return Padding( // ... child: FormBuilder( key: _formKey, child: SizedBox( // ... ), ), ); } }
4. 验证GoRouter初始化
确保AppRouterConfig的router在应用启动时已正确初始化,比如在MaterialApp中直接初始化:
MaterialApp.router( routerConfig: AppRouterConfig().router, // 其他配置 )
5. 检查RegisterViewModel状态管理
确保RegisterViewModel继承自ChangeNotifier(如果使用状态管理),并在BaseView中正确传递:
class RegisterViewModel extends ChangeNotifier { late PageController pageController; }
内容的提问来源于stack exchange,提问作者MertYavuz
相关产品推荐
相关产品推荐

