flutterflutter-getxauto-route

Flutter GetX and AutoRoute: Get.context is null (Get is not initialized properly?)


I use GetX Flutter framework, but want use use AutoRoute for navigation. However, if I use AutoRoute, Get.context is always null in my widgets therefore things such as Get.width cause a crash.

Example/steps to reproduce (uses Flutter 3.13.4):

pubspec.yml

name: testtest
description: A new Flutter project.
publish_to: 'none'

version: 1.0.0+1

# All versions are locked to the version I used when posting this question
environment:
  sdk: '3.1.2'

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: 1.0.2
  get: 4.6.6
  auto_route: 7.8.3

dev_dependencies:
  flutter_test:
    sdk: flutter

  flutter_lints: 2.0.0
  auto_route_generator: 7.3.1
  build_runner:

flutter:

  uses-material-design: true

lib/main.dart

import 'package:flutter/material.dart';
import 'package:auto_route/auto_route.dart';
import 'package:get/get_navigation/src/root/get_material_app.dart';
import 'package:testtest/home_screen.dart';

import 'main.gr.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  MyApp({super.key});

  final _appRouter = AppRouter();

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp.router( // (1)
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      routerDelegate: _appRouter.declarativeDelegate(routes: (handler) => [HomeRoute()],), // (2)
      routeInformationParser: _appRouter.defaultRouteParser(), // (3)
      // home: HomeScreen(), // (4)
    );
  }
}

@AutoRouterConfig()
class AppRouter extends $AppRouter {

  @override
  List<AutoRoute> get routes => [
    AutoRoute(path: "/HomeRoute", page: HomeRoute.page),
  ];
}

home_screen.dart

import 'package:auto_route/annotations.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';

@RoutePage()
class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {

  @override
  Widget build(BuildContext context) {
    double? width = null;
    try {
      width = Get.width;
    }catch(_){}
    return Scaffold(
      body: SafeArea(child:
        Text(
"""Context: ${Get.context?.toString() ?? "null"}
width: ${width ?? "Error"}
""")
      ),
    );
  }
}

and don't forget to run code generation for AutoRoute:

flutter packages pub run build_runner build

In the example above, a empty screen with this text will appear:

Context: null
width: Error

because Get.context was null and Get.width caused and exception.

However, If you change GetMaterialApp.router to GetMaterialApp on line marked (1), comment out lines (2) and (3) and uncomment line (4), AutoRoute will no longer be used and everything works as expected - Get.context is no longer null and Get.width return correct width.


Solution

  • The reason why Get.context is null in your AutoRoute example is because GetX and AutoRoute are both navigation libraries, and they conflict with each other when used together.

    To fix this, you have two options:

    Use GetX for navigation. If you want to use GetX for navigation, then you should remove AutoRoute from your project and use GetX's Get.to() and Get.back() methods to navigate between screens. Use AutoRoute for navigation. If you want to use AutoRoute for navigation, then use AutoRoute's Router() to navigate between screens. If you choose to use AutoRoute for navigation, then you will need to use AutoRoute's own context object to access context-aware services. This is because AutoRoute creates its own widget tree, and GetX's Get.context object will not be available within that widget tree.

    To access AutoRoute's context object, you can use the RouteData.context property. This property is available to all AutoRoute widgets.

    Below code is an example of how to use the RouteData.context property to get the screen width in an AutoRoute widget:

    import 'package:flutter/material.dart';
    import 'package:auto_route/auto_route.dart';
    
    class HomeScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final width = RouteData.of(context).context?.width;
    
        return Scaffold(
          body: SafeArea(
            child: Text('Screen width: $width'),
          ),
        );
      }
    }
    

    I hope this helps!