flutterdartflutter-dependenciesauto-route

TextFormField not working properly with AutoRoute


I am trying to set up AutoRoute for my Flutter app. it is pretty simple and I haven't added a lot of pages yet. However, in the pages I have made, the TextFormField doesn't work. Whenever I tap the input field, the sceen flickers (possibly the widget rebuilding) and the keyboard shows up for a short time and then nothing happens. If I try to do the same on a page other than the route with initial: true , I get navigated to the initial page. I confirmed that it has something to do with the autoroute because if i remove it from the app the fields start working properly. I also added buttons on the pages to navigate among them. they work properly.

main.dart

void main() async {
  await ScreenUtil.ensureScreenSize();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    ScreenUtil.init(context, designSize: const Size(AppConstants.appWidth, AppConstants.appHeight));
    final _appRouter = AppRouter();
    return MaterialApp.router(
      routerConfig: _appRouter.config(),
      debugShowCheckedModeBanner: false,
      title: 'Book AI',
      theme: AppTheme.darkTheme(),
      themeMode: ThemeMode.system,
      //home: const TestingScreen()
    );
  }
}

router.dart:

import 'package:auto_route/auto_route.dart';
import 'package:book_ai/presentation/pages/route_test.dart';
import 'package:book_ai/presentation/pages/test.dart';
// import 'package:book_ai/presentation/router/router.dart';


part 'router.gr.dart';  

@AutoRouterConfig()
class AppRouter extends _$AppRouter {
  @override
  List<AutoRoute> get routes => [
    AutoRoute(page: TestingRoute.page, initial: true),
    AutoRoute(page: RoutingTestRoute.page)

  ];
}

test.dart that contains the input fields I created:

@RoutePage()
class TestingScreen extends StatefulWidget {
  const TestingScreen({super.key});
  @override
  State<TestingScreen> createState() => _TestScreenState();
}

class _TestScreenState extends State<TestingScreen> {
  final TextEditingController textController = TextEditingController();
  final TextEditingController testController = TextEditingController();
  @override
  Widget build(BuildContext context) {
    ScreenUtil.init(context, designSize: const Size(AppConstants.appWidth, AppConstants.appHeight));

    return Scaffold(
      resizeToAvoidBottomInset: true,
      backgroundColor: Colors.white,
      appBar: AppBar(
        backgroundColor: Theme.of(context).focusColor,
        title: const Text('Testing page'),
      ),
      body: Container(
        decoration:const BoxDecoration(
          image: DecorationImage(
            image: AssetImage('assets/images/bg-dark.png'), // Replace with your image path
            fit: BoxFit.cover,
          ),
        ),
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              FilterButton(text: "hello", active: false, onPressed: () {  }),
              // FilterButton(text: "hello", active: false, onPressed: () { }),
              PrimaryButton(text: "hello", onPressed: () { context.router.push(const RoutingTestRoute());}),
              SearchInput(controller: textController, hintText: 'Search for something'),
              TextFormField(controller: testController,  decoration: InputDecoration(
                      hintText: 'this is a test',
                      border: InputBorder.none,
                      hintStyle: Theme.of(context).textTheme.titleMedium
                    ),)
            ],
          ),
        ),
      ),
    );
  }
}

My custom input widget just in case someone needs to take a look. I think there isnt an issue with it since even a plain TextFormField widget is not working:

class SearchInput extends StatefulWidget {
  final TextEditingController controller;
  final String hintText;
  final VoidCallback? onSearchPressed;

  SearchInput({
    required this.controller,
    required this.hintText,
    this.onSearchPressed,
  });

  @override
  State<SearchInput> createState() => _SearchInputState();
}

class _SearchInputState extends State<SearchInput> {
  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(6.w),
        color: Theme.of(context).disabledColor, // Adjust the flopacity as needed
      ),
      child: ClipRRect(
        borderRadius: BorderRadius.circular(6.w),
        child: BackdropFilter(
          filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
          child: Row(
            children: [
              Expanded(
                child: Padding(
                  padding: EdgeInsets.symmetric(horizontal: 16.w),
                  child: Form(
                    child: TextFormField(
                      controller: widget.controller,
                      decoration: InputDecoration(
                        hintText: widget.hintText,
                        border: InputBorder.none,
                        hintStyle: Theme.of(context).textTheme.titleMedium
                      ),
                    ),
                  ),
                ),
              ),
              IconButton(
                icon: const Icon(Icons.search),
                onPressed: widget.onSearchPressed,
                color: Theme.of(context).primaryColor, // fix issue that it shows as gray
                
              ),
            ],
          ),
        ),
      ),
    );
  }
}


I tried adding a Form widget around the inputs, passing a key to each of them but none of them work.


Solution

  • Do not initiate AppRouter inside the build method.

    From the readme of auto_route:

    after you run the generator your router class will be generated, hook it up with MaterialApp.

    // assuming this is the root widget of your App                 
    class App extends StatelessWidget {            
      // make sure you don't initiate your router                
      // inside of the build function.                
      final _appRouter = AppRouter();            
                
      @override            
      Widget build(BuildContext context){            
        return MaterialApp.router(            
          routerConfig: _appRouter.config(),         
        );            
      }            
    }