0

I have got a DefaultTabController with a couple of tabs that the user can swipe or press an ElevatedButton to proceed to the next slide, my issue is that I don't know how to change the button's label when the user reaches the last tab using swipes.

Using a stateful widget I managed to change the label when the user presses the button but it doesn't work if the user swipes. Is it possible to change the button when the user reaches the last tab?

class SlidesWidget extends StatelessWidget {
  static List<Slide> slides = [
    const Slide(
        text: 'Welcome to ..'),
    const Slide(
        text: 'Ready to discover your city?')
  ];

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: slides.length,
      child: Builder( // Builder here, otherwise `DefaultTabController.of(context)` returns null.
        builder: (BuildContext context) => Padding(
          padding: const EdgeInsets.all(8.0),
          child: SafeArea(
            child: Column(
              children: [
                const TabPageSelector(
                  selectedColor: Colors.white,
                ),
                Expanded(
                  flex: 100,
                  child: TabBarView(
                    children: slides,
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(18.0),
                  child: ElevatedButton(
                    onPressed: () {
                      final TabController controller =
                          DefaultTabController.of(context)!;
                      if (!controller.indexIsChanging &&
                          controller.index < slides.length - 1) {
                        // Go to next slide if exists
                        controller.index++;
                      }
                    },
                    child: Text('Next'), // <== on last slide should change label and do other things
                  ),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}
Md. Yeasin Sheikh
  • 54,221
  • 7
  • 29
  • 56
6pounder
  • 67
  • 7

1 Answers1

1

I will recommend using StatefulWidget, also you can use inline StatefulBuilder to update the UI. And using TabController is handy instead of calling it multiple times, and there is risk of getting null for DefaultTabController.

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

  @override
  State<SlidesWidget> createState() => _SlidesWidgetState();
}

class _SlidesWidgetState extends State<SlidesWidget>
    with SingleTickerProviderStateMixin {
  late TabController controller;
  List<Slide> slides = [
    const Slide(text: 'Welcome to ..'),
    const Slide(text: 'Ready to discover your city?')
  ];
  @override
  void initState() {
    super.initState();
    controller = TabController(length: slides.length, vsync: this)
      ..addListener(() {
        setState(() {});
      });
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Padding(
      padding: const EdgeInsets.all(8.0),
      child: SafeArea(
        child: Column(
          children: [
         
            TabPageSelector(
              controller: controller,
              selectedColor: Colors.white,
            ),
            Expanded(
              flex: 100,
              child: TabBarView(
                controller: controller,
                children: slides,
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(18.0),
              child: ElevatedButton(
                onPressed: () {
                  if (!controller.indexIsChanging &&
                      controller.index < slides.length - 1) {
                    // Go to next slide if exists
                    controller.index++;
                  }
                },
                child: Text(controller.index == 1 ? 'start' : "Next"), //
              ),
            )
          ],
        ),
      ),
    ));
  }
}

More about TabController and I think you will also like IndexedStack for this case.

Md. Yeasin Sheikh
  • 54,221
  • 7
  • 29
  • 56
  • I'm really new to Dart / Flutter, sorry for the question but why addListener have two dots instead of one? – 6pounder Jul 18 '22 at 15:48
  • Single dot is to refer to an class variable/method but we are assigning `controller` which itself needed to assign, Using `..` provide use this flexibility and it says include this listener but return `TabController`. You can also do it separately on next line like `controller.addListener` you can check [this](https://stackoverflow.com/q/49447736/10157127) – Md. Yeasin Sheikh Jul 18 '22 at 15:53
  • Thank you! Just a thing, it's working fine but it changes the button only when the slide animation is over, is it possible to change the button as the tab slides? – 6pounder Jul 18 '22 at 16:00
  • based on tab sliding value? – Md. Yeasin Sheikh Jul 18 '22 at 16:06
  • Yes, with button it changes almost instantly, with the sliding thing it waits for the animation to end and it takes like a second – 6pounder Jul 18 '22 at 16:12
  • Index getter is set end of the animation that's why we are getting this, maybe others way it can be handled – Md. Yeasin Sheikh Jul 18 '22 at 16:15