You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,
          ),
        ],
      ),
    );
  }
}

问题定位

  1. GoRouter上下文传递异常:错误触发点在go_router的builder方法,说明路由跳转时无法正确构建目标页面,大概率是页面依赖的某个实例为空。
  2. BaseState方法空值风险:RegisterScreenOne中调用的dynamicHeight、dynamicWidth、currentTheme依赖BaseState的上下文,如果BaseState未正确绑定context,会直接抛出空值错误。
  3. PageController初始化问题:_viewModel.pageController在initState中初始化,但如果BaseView未正确持有_viewModel,会导致PageController为空,PageView无法正常渲染。
  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 18:29:51