Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic children for TabView in flutter

I'm trying to build a Tabbed View that has lists as children.

Both the Category labels and the lists content will be fetched from a database.

I am passing the labels from the caller page and successfully passing them as a List. Now I'm trying to load my lists, and I have built a Widget (myList) that returns successfully a Future ListView.

The problems are two:

  1. Every time i swipe left or right, the list rebuilds itself, while I would like to have it built only once
  2. How can I use the code I made to have the tabs' children actually reflect the labels and are loaded dinamically according to how many categories i have?

Right now my code is this:

import 'package:flutter/material.dart';
import 'package:flutter_app/ui/menu_category_list.dart';

// Each TabBarView contains a _Page and for each _Page there is a list
// of _CardData objects. Each _CardData object is displayed by a _CardItem.

List<Tab> Tabs(List<String> l){
  List<Tab> list;
  for (String c in l) {
    list.add(new Tab(text: c));
  }
  return list;
}



class TabsDemo extends StatelessWidget {

  const TabsDemo({ Key key , this.categorie}) : super(key: key);

  final List<Tab> categorie;

  @override
  Widget build(BuildContext ctxt) {
    return new MaterialApp(
      title: "Nice app",
      home: new DefaultTabController(
      length: 5,
      child: new Scaffold(
        appBar: new AppBar(
          title: new Text("Title"),
          bottom: new TabBar(
            tabs:
              categories,
              //new Tab(text: "First Tab"),
              //new Tab(text: "Second Tab"),

          ),

        ),
        body: new TabBarView(
            children: [
              new MenuCategoryList(),
              new MenuCategoryList(),
              new MenuCategoryList(),
              new MenuCategoryList(),
              new MenuCategoryList()
            ]
        )
      ),
    )
    );
  }
}

currently result

Thanks a lot in advance

like image 848
Andrea Grippi Avatar asked Jun 30 '18 19:06

Andrea Grippi


3 Answers

You can use List<E>.generate to achieve this.

import 'package:flutter/material.dart';

Say you have a set of categories passed from your caller page. And let's say this is your list of categories.

List<String> categories = ["a", "b", "c", "d", "e", "f", "g", "h"];

Then you can do something like this to achieve what you desire.

class TabsDemo extends StatefulWidget {
  @override
  _TabsDemoState createState() => _TabsDemoState();
}

class _TabsDemoState extends State<TabsDemo> {
  TabController _controller;

  @override
  void initState() {
    super.initState();
  }

    @override
    Widget build(BuildContext ctxt) {
      return new MaterialApp(
        home: DefaultTabController(
            length: categories.length,
            child: new Scaffold(
              appBar: new AppBar(
                title: new Text("Title"),
                bottom: new TabBar(
                  isScrollable: true,
                    tabs: List<Widget>.generate(categories.length, (int index){
                  print(categories[0]);
                  return new Tab(icon: Icon(Icons.directions_car), text: "some random text");

                }),

              ),
            ),

        body: new TabBarView(
             children: List<Widget>.generate(categories.length, (int index){
                print(categories[0]);
                return new Text("again some random text");

             }),
          )
       ))
      );
  }

You can also set different set of widgets as the Tab's view. You can create a list of pages and follow the same method.

like image 197
Chaythanya Nair Avatar answered Sep 22 '22 15:09

Chaythanya Nair


Absolutely true List<E>.generate best solution to solve.

Problems arise if you need to modify the arrays. They consist in the fact that when modifying an array you do not have the opportunity to use the same controller.

enter image description here

You can use the next custom widget for this case:

import 'package:flutter/material.dart';

  void main() => runApp(MyApp());

  class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Flutter Demo',
        home: MyHomePage(),
      );
    }
  }

  class MyHomePage extends StatefulWidget {
    @override
    _MyHomePageState createState() => _MyHomePageState();
  }

  class _MyHomePageState extends State<MyHomePage> {
    List<String> data = ['Page 0', 'Page 1', 'Page 2'];
    int initPosition = 1;

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        body: SafeArea(
          child: CustomTabView(
            initPosition: initPosition,
            itemCount: data.length,
            tabBuilder: (context, index) => Tab(text: data[index]),
            pageBuilder: (context, index) => Center(child: Text(data[index])),
            onPositionChange: (index){
              print('current position: $index');
              initPosition = index;
            },
            onScroll: (position) => print('$position'),
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            setState(() {
              data.add('Page ${data.length}');
            });
          },
          child: Icon(Icons.add),
        ),
      );
    }
  }

  /// Implementation

  class CustomTabView extends StatefulWidget {
    final int itemCount;
    final IndexedWidgetBuilder tabBuilder;
    final IndexedWidgetBuilder pageBuilder;
    final Widget stub;
    final ValueChanged<int> onPositionChange;
    final ValueChanged<double> onScroll;
    final int initPosition;

    CustomTabView({
      @required this.itemCount,
      @required this.tabBuilder,
      @required this.pageBuilder,
      this.stub,
      this.onPositionChange,
      this.onScroll,
      this.initPosition,
    });

    @override
    _CustomTabsState createState() => _CustomTabsState();
  }

  class _CustomTabsState extends State<CustomTabView> with TickerProviderStateMixin {
    TabController controller;
    int _currentCount;
    int _currentPosition;

    @override
    void initState() {
      _currentPosition = widget.initPosition ?? 0;
      controller = TabController(
        length: widget.itemCount,
        vsync: this,
        initialIndex: _currentPosition,
      );
      controller.addListener(onPositionChange);
      controller.animation.addListener(onScroll);
      _currentCount = widget.itemCount;
      super.initState();
    }

    @override
    void didUpdateWidget(CustomTabView oldWidget) {
      if (_currentCount != widget.itemCount) {
        controller.animation.removeListener(onScroll);
        controller.removeListener(onPositionChange);
        controller.dispose();

        if (widget.initPosition != null) {
          _currentPosition = widget.initPosition;
        }

        if (_currentPosition > widget.itemCount - 1) {
            _currentPosition = widget.itemCount - 1;
            _currentPosition = _currentPosition < 0 ? 0 : 
            _currentPosition;
            if (widget.onPositionChange is ValueChanged<int>) {
               WidgetsBinding.instance.addPostFrameCallback((_){
                if(mounted) {
                  widget.onPositionChange(_currentPosition);
                }
               });
            }
         }

        _currentCount = widget.itemCount;
        setState(() {
          controller = TabController(
            length: widget.itemCount,
            vsync: this,
            initialIndex: _currentPosition,
          );
          controller.addListener(onPositionChange);
          controller.animation.addListener(onScroll);
        });
      } else if (widget.initPosition != null) {
        controller.animateTo(widget.initPosition);
      }

      super.didUpdateWidget(oldWidget);
    }

    @override
    void dispose() {
      controller.animation.removeListener(onScroll);
      controller.removeListener(onPositionChange);
      controller.dispose();
      super.dispose();
    }

    @override
    Widget build(BuildContext context) {
      if (widget.itemCount < 1) return widget.stub ?? Container();

      return Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          Container(
            alignment: Alignment.center,
            child: TabBar(
              isScrollable: true,
              controller: controller,
              labelColor: Theme.of(context).primaryColor,
              unselectedLabelColor: Theme.of(context).hintColor,
              indicator: BoxDecoration(
                border: Border(
                  bottom: BorderSide(
                    color: Theme.of(context).primaryColor,
                    width: 2,
                  ),
                ),
              ),
              tabs: List.generate(
                widget.itemCount,
                    (index) => widget.tabBuilder(context, index),
              ),
            ),
          ),
          Expanded(
            child: TabBarView(
              controller: controller,
              children: List.generate(
                widget.itemCount,
                    (index) => widget.pageBuilder(context, index),
              ),
            ),
          ),
        ],
      );
    }

    onPositionChange() {
      if (!controller.indexIsChanging) {
        _currentPosition = controller.index;
        if (widget.onPositionChange is ValueChanged<int>) {
          widget.onPositionChange(_currentPosition);
        }
      }
    }

    onScroll() {
      if (widget.onScroll is ValueChanged<double>) {
        widget.onScroll(controller.animation.value);
      }
    }
  }
like image 37
Yuriy Luchaninov Avatar answered Sep 25 '22 15:09

Yuriy Luchaninov


You can use dynamic children using for loop within your Tabbarview Widget

List<String> categories = ["category 1" , "category 2", "category 3",];
return TabBarView(
  children:[
    for(var category in categories)
      Text(category), // this widget will show a text with specific category. You can use any other widget
  ],
);
like image 23
Hamza Ali Avatar answered Sep 22 '22 15:09

Hamza Ali