Home > Enterprise >  Flutter : How to access states and controllers of pre-made widget that have already been defined by
Flutter : How to access states and controllers of pre-made widget that have already been defined by

Time:10-01

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  MyApp({Key? key}) : super(key: key);

  final GlobalKey listViewKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    //listViewKey.currentState.

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: Column(
          children: [
            Container(
              height: 100.0,
              child: ElevatedButton( //Outer button
                style: ElevatedButton.styleFrom(
                  fixedSize: Size(
                    300.0,
                    100.0,
                  ),
                ),
                child: Text('top'),
                onPressed: () {
                  //What should I do?
                },
              ),
            ),
            Expanded(
              child: CustomListView(
                key: listViewKey,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class CustomListView extends StatefulWidget {
  const CustomListView({Key? key}) : super(key: key);

  @override
  State<CustomListView> createState() => _CustomListViewState();
}

class _CustomListViewState extends State<CustomListView> {
  late final ScrollController ctrl;
  double position = 0.0;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    ctrl = ScrollController()
      ..addListener(() {
        print(ctrl.offset);
        setState((){
          position = ctrl.offset;
        });
      });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Center(
          child: Text(
            position.toString(),
          ),
        ),
        Expanded(
          child: ListView(
            controller: ctrl,
            children: [
              Container(
                height: 500.0,
                color: Colors.red,
              ),
              Container(
                height: 500.0,
                color: Colors.green,
              ),
              Container(
                height: 500.0,
                color: Colors.yellow,
              ),
              ElevatedButton( // Inner button
                onPressed: () async {
                  ctrl.animateTo(
                    0.0,
                    duration: Duration(
                      seconds: 1,
                    ),
                    curve: Curves.ease,
                  );
                },
                child: Text('top'),
              )
            ],
          ),
        ),
      ],
    );
  }
}

For example, let's say we made an app like the one above using a ready-made widget called CustomListView.

We can control scrolling by pressing a button(Inner button) inside the CustomListView. This is fine.

but what if, for example, we want to control the scrolling of a ListView inside a CustomListView when we press the Outer button?


↓For example, in the CustomListView definition, make the CustomListView receive a ScrollController:

class CustomListView extends StatefulWidget {
  const CustomListView({
    Key? key,
    required this.ctrl,
  }) : super(key: key);

  final ScrollController ctrl;

  @override
  State<CustomListView> createState() => _CustomListViewState();
}

class _CustomListViewState extends State<CustomListView> {
  late final ScrollController ctrl;
  double position = 0.0;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    ctrl = widget.ctrl;

    /*
    ScrollController()
      ..addListener(() {
        print(ctrl.offset);
        setState((){
          position = ctrl.offset;
        });
      });
    */
  }

↓And in the outer MyApp declare a ScrollController and pass it to the CustomListView.

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

class MyApp extends StatefulWidget {
  MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final GlobalKey listViewKey = GlobalKey();
  late final ScrollController ctrl;
  ...

////////////////////////////////////////////////////
//in the build method of _MyAppState

            Expanded(
              child: CustomListView(
                key: listViewKey,
                ctrl: ctrl,
              ),
            ),

Now we can control the scrolling of the CustomListView from the outer MyApp, but for that we need to modify the CustomListView definition.

I think there are situations where we can't change the code, such as when the CustomListView is not our own.

If so, is there a way to control the CustomListView's scrolling and get the scroll position from the outer MyApp? Does it mean that it is impossible because there is no way?


It just came to my mind, could NotificationListner/ScrollNotification be the solution? Or if there is another way, please let me know.

CodePudding user response:

You can access the scrollController inside _CustomListViewState with the listViewKey that you provided to the CustomListView widget like this:

ScrollController controller = (listViewKey.currentState as _CustomListViewState).ctrl

You may need to remove _ if the _CustomListViewState is inside another file.

If you wrote the CustomListView then it is best practice to accept an external scrollController. If not provided externally then you can create an instance of scrollController inside CustomListView.

  • Related