skip to Main Content

I’m having trouble deciding where to define my controller which is used for scrolling.

My Widget is stateless (and I’d like to keep it like that).

Should I define the controller inside the build method, outside the Widget itself or somewhere else?

P.S. Does the fact that the Widget is stateless mean that I can’t dispose of the controller?

I tried creating the controller outside of the Widget but that doesn’t seem very good design wise (global variables…)

2

Answers


  1. If you have the capability to use stacked architecture, you could do as follows.

    In pubspec

    stacked:
    stacked_services:
    

    Your Stateless View

    import 'package:stacked/stacked.dart';
    import 'package:stacked/stacked_annotations.dart';
    
    @FormView(fields: [
      FormTextField(name: yourController),
    ])
    
    Class YourView extends StatelessWidget with $YourView {
    
     @override
     Widget build(BuildContext context) {
        return ViewModelBuilder<MetricFormViewModel>.reactive(
          onViewModelReady: (model) async {
            return syncFormWithViewModel(model);
      },
      builder: (context, model, child) => YourViewWidget();
    }
    );}
    

    Then stacked will auto generate the necessary code for you to access your controller outside of your stateless widget by running:

    stacked generate
    

    in the terminal.

    To use the generated code import the file something like

    import 'your_view.form.dart';
    
    Login or Signup to reply.
  2. You could use a ChangeNotifier. So wrap you widget in a ChangeNotifierProvider (requires the provider package) and then initialise the controller in the ChangeNotifierProvider. Then when you build you stateless widget, you can attach the controller to the widget, and then listen to events from the controller, call notifyListeners(). For example, this is with a TextEditingController.

    ChangeNotifierProvider

    class TextControllerNotifier extends ChangeNotifier {
      TextEditingController textController = TextEditingController();
    
      TextControllerNotifier() {
        textController.addListener(_onTextChanged);
      }
    
      void _onTextChanged() {
        notifyListeners();
      }
    
      @override
      void dispose() {
        textController.removeListener(_onTextChanged);
        textController.dispose();
        super.dispose();
      }
    }
    

    Parent widget

    ChangeNotifierProvider(
      create: (context) => TextControllerNotifier(),
      child: Child(),
    ),
    

    Stateless child

    class Child extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            // Consumer listens to changes in TextControllerNotifier
            child: Consumer<TextControllerNotifier>(
              builder: (context, textNotifier, child) {
                return Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Padding(
                      padding: const EdgeInsets.all(16.0),
                      child: TextField(
                        controller: textNotifier.textController,
                        decoration: InputDecoration(labelText: 'Enter text'),
                      ),
                    ),
                    // Text widget listens to value changes because it's in a Consumer
                    Text('Current Text: ${textNotifier.textController.text}'),
                  ],
                );
              },
            ),
          ),
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search