jonataslaw / getx

Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get.
MIT License
10.34k stars 1.62k forks source link

About (DefaultTabController + TabBar + TabBarView ) GetxController initialization problem #1341

Open TBoyLi opened 3 years ago

TBoyLi commented 3 years ago

Describe the bug TabBarView children use the same View with AutomaticKeepAliveClientMixin, TabBarView when trying to switch, the Controller will not initialized again, Controller sets the tag to be reinitialized. At this point, the TabBarView switches and tries, the Controller data changes, but the View data doesn't change.

Reproduction code NOTE: THIS IS MANDATORY, IF YOUR ISSUE DOES NOT CONTAIN IT, IT WILL BE CLOSED PRELIMINARY)

example:

**DefaultTabController + TabBar + TabBarView**
class ArticleCategoryView extends GetView<ArticleCategoryController> {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: controller.tree.children.length,
      initialIndex: controller.index,
      child: Scaffold(
        appBar: AppBar(
          title: Text(controller.tree.name),
          centerTitle: true,
          bottom: TabBar(
            isScrollable: true,
            tabs: List.generate(
              controller.tree.children.length,
              (index) => Tab(
                text: controller.tree.children[index].name,
              ),
            ),
          ),
        ),
        body: TabBarView(
          children: List.generate(
            controller.tree.children.length,
            (index) {
              return ArticleListView(
                cid: controller.tree.children[index].id,
              );
            },
          ),
        ),
      ),
    );
  }
}

class ArticleListView extends StatefulWidget {
  final int cid;

  ArticleListView({Key key, this.cid}) : super(key: key);

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

class _ArticleListViewState extends State<ArticleListView>
    with AutomaticKeepAliveClientMixin {
  @override
  Widget build(BuildContext context) {
    super.build(context);
    Get.put(
      ArticleListController(widget.cid),
      tag: 'ArticleListController-${widget.cid}',
    );
    return ArticleListContainer(widget.cid);
  }

  @override
  bool get wantKeepAlive => true;
}

class ArticleListContainer extends GetView<ArticleListController> {
  final int cid;

  ArticleListContainer(this.cid);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GetBuilder<ArticleListController>(
        init: ArticleListController(cid),
        builder: (_) {
          if (controller.loading) {
            return SkeletonList(
              builder: (context, index) => ArticleSkeletonItem(),
            );
          } 
          // else if (controller.isError && controller.data.isEmpty) {
          //   return ViewStateErrorWidget(
          //       error: controller.viewStateError,
          //       onPressed: controller.getData);
          // } else if (controller.isEmpty) {
          //   return ViewStateEmptyWidget(onPressed: controller.getData);
          // }
          return EasyRefresh(
            // controller: controller.easyRefreshController,
            header: ClassicalHeader(),
            footer: ClassicalFooter(),
            // onRefresh: controller.refreshData,
            // onLoad: controller.loadMoreData,
            child: ListView.builder(
              itemCount: controller.data.length,
              itemBuilder: (context, index) {
                Article item = controller.data[index];
                return Text('${item.title}');
              },
            ),
          );
        },
      ),
    );
  }
}

class ArticleListController extends GetxController {
  final int cid;

  ArticleListController(this.cid);

  List<Article> data = [];

  bool loading = false;

  @override
  void onInit() async {
    loading = true;
    data = await loadData(pageNum: 0);
    print('getData length -> ${data.length}');
    loading = false;
    update();
    super.onInit();
  }

  Future<List<Article>> loadData({int pageNum}) async {
    return await WanAndroidRepository.fetchArticles(pageNum, cid: cid);
  }
}

To Reproduce Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior TabView toggle is the update of data to the View.

Screenshots image image

Flutter Version:

➜  ~ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.0.4, on macOS 11.2.3 20D91 darwin-x64, locale zh-Hans-CN)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[✓] Xcode - develop for iOS and macOS
[✓] Chrome - develop for the web
[✓] Android Studio
[✓] Android Studio (version 3.6)
[✓] IntelliJ IDEA Ultimate Edition (version 2020.3.1)
[✓] VS Code (version 1.55.2)
[✓] Connected device (2 available)

• No issues found!

Getx Version:

get: 4.1.4

Describe on which device you found the bug: iPhone 12 Pro Max (mobile)

thomasgi1 commented 2 years ago

I think, the problem is:void onInit() async .... I don't see any await onInit() within Getx. So, GetView.build() is called before GetxController.onInit() is complete.

sachin-rai1 commented 2 years ago

Did you get any solution for this bug ??

nenaqui commented 2 years ago

Hola alguien quiere ser mi patrocinador