如何在 flutter 中从另一个页面调用选项卡控制器

How to call tab controller from another page in flutter

我有带 4 个选项卡的底部导航栏,一切正常,但我有另一个 class 调用 BannerImageItem,我想从那里调用选项卡 2。我尝试使用 Gloabal 键,但从选项卡控制器中获取 null 请帮助我,我被卡住了。

我有带 4 个选项卡的底部导航栏,一切正常,但我有另一个 class 调用 BannerImageItem,我想从那里调用选项卡 2。我尝试使用 Gloabal 键,但从选项卡控制器中获取 null 请帮助我,我被卡住了。

const int tabCount = 3;
const int turnsToRotateRight = 1;
const int turnsToRotateLeft = 3;

class MainTabControlDelegate {
  int index;
  Function(String nameTab) changeTab;
  Function(int index) tabAnimateTo;

  static MainTabControlDelegate _instance;
  static MainTabControlDelegate getInstance() {
    return _instance ??= MainTabControlDelegate._();
  }

  MainTabControlDelegate._();
}

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

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

class MainTabsState extends State<MainTabs>
    with
        WidgetsBindingObserver,
        SingleTickerProviderStateMixin,
        AfterLayoutMixin {
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

  final PageStorageBucket bucket = PageStorageBucket();

  final StreamController<String> _controllerRouteWeb =
      StreamController<String>.broadcast();

  final _auth = FirebaseAuth.instance;
  var tabData;
  Map saveIndexTab = Map();

  FirebaseUser loggedInUser;
  bool isAdmin = false;

  final List<Widget> _tabView = [];
  TabController tabController;

  @override
  void afterFirstLayout(BuildContext context) {
    loadTabBar(context);
  }

  Widget tabView(Map<String, dynamic> data) {
    switch (data['layout']) {
      case 'category':
        return CategoriesScreen(
          key: Key("category"),
          layout: data['categoryLayout'],
          categories: data['categories'],
          images: data['images'],
          showChat: data['showChat'],
        );
      case 'search':
        return SearchScreen(key: Key("search"), showChat: data['showChat']);
      case 'cart':
        return CartScreen(showChat: data['showChat']);
      case 'profile':
        return UserScreen(
            settings: data['settings'],
            background: data['background'],
            showChat: data['showChat']);
      case 'blog':
        return HorizontalSliderList(config: data);
      case 'wishlist':
        return screen.WishList(canPop: false, showChat: data['showChat']);
      case 'page':
        return WebViewScreen(
            title: data['title'], url: data['url'], showChat: data['showChat']);
      case 'html':
        return StaticSite(data: data['data'], showChat: data['showChat']);
      case 'static':
        return StaticPage(data: data['data'], showChat: data['showChat']);
      case 'postScreen':
        return PostScreen(
            pageId: data['pageId'],
            pageTitle: data['pageTitle'],
            isLocatedInTabbar: true,
            showChat: data['showChat']);
      case 'dynamic':
      default:
        return HomeScreen();
    }
  }

  void changeTab(String nameTab) {
    if (kLayoutWeb) {
      _controllerRouteWeb.sink
          .add(nameTab.contains("/") ? nameTab : '/$nameTab');
    } else {
      tabController?.animateTo(saveIndexTab[nameTab] ?? 0);
    }
  }

  void loadTabBar(context) {
    tabData = Provider.of<AppModel>(context, listen: false).appConfig['TabBar']
        as List;

    for (var i = 0; i < tabData.length; i++) {
      Map<String, dynamic> _dataOfTab = Map.from(tabData[i]);

      saveIndexTab[_dataOfTab['layout']] = i;
      _tabView.add(tabView(_dataOfTab));
    }

    setState(() {
      tabController = TabController(length: _tabView.length, vsync: this);
    });

    if (MainTabControlDelegate.getInstance().index != null) {
      tabController.animateTo(MainTabControlDelegate.getInstance().index);
    } else {
      MainTabControlDelegate.getInstance().index = 0;
    }

    // Load the Design from FluxBuilder
    tabController.addListener(() {
      eventBus.fire('tab_${tabController.index}');
      if (_tabView[tabController.index] is SearchScreen) {
        eventBus.fire(UpdateSearchFilterEvent());
      }
      MainTabControlDelegate.getInstance().index = tabController.index;
    });
  }

  Future<void> getCurrentUser() async {
    try {
      //Provider.of<UserModel>(context).getUser();
      final user = await _auth.currentUser();
      if (user != null) {
        setState(() {
          loggedInUser = user;
        });
      }
    } catch (e) {
      printLog("[tabbar] getCurrentUser error ${e.toString()}");
    }
  }

  bool checkIsAdmin() {
    if (loggedInUser.email == adminEmail) {
      isAdmin = true;
    } else {
      isAdmin = false;
    }
    return isAdmin;
  }

  @override
  void initState() {
    if (!kIsWeb) {
      getCurrentUser();
    }
    MainTabControlDelegate.getInstance().changeTab = changeTab;
    MainTabControlDelegate.getInstance().tabAnimateTo = (int index) {
      tabController?.animateTo(index);
    };
    super.initState();

    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    tabController?.dispose();
    _controllerRouteWeb?.close();
    WidgetsBinding.instance.removeObserver(this);

    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.paused) {
      // went to Background
    }
    if (state == AppLifecycleState.resumed) {
      // came back to Foreground
      final appModel = Provider.of<AppModel>(context, listen: false);
      if (appModel.deeplink?.isNotEmpty ?? false) {
        if (appModel.deeplink['screen'] == 'NotificationScreen') {
          appModel.deeplink = null;
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => NotificationScreen()),
          );
        }
      }
    }

    super.didChangeAppLifecycleState(state);
  }

  @override
  Widget build(BuildContext context) {
    printLog('[tabbar] ============== tabbar.dart DASHBOARD ==============');
    final isDesktop = isDisplayDesktop(context);
    Utils.setStatusBarWhiteForeground(false);
    kLayoutWeb = (kIsWeb || isDesktop);

    if (_tabView.isEmpty) {
      return Container(
        color: Colors.white,
        child: kLoadingWidget(context),
      );
    }

    return renderBody(context);
  }

  Widget renderBody(BuildContext context) {
    final ThemeData theme = Theme.of(context);

    if (kLayoutWeb) {
      final isDesktop = isDisplayDesktop(context);

      return Scaffold(
        backgroundColor: Theme.of(context).backgroundColor,
        body: SafeArea(
          // For desktop layout we do not want to have SafeArea at the top and
          // bottom to display 100% height content on the accounts view.
          top: !isDesktop,
          bottom: !isDesktop,
          child: Theme(
              // This theme effectively removes the default visual touch
              // feedback for tapping a tab, which is replaced with a custom
              // animation.
              data: theme.copyWith(
                splashColor: Colors.transparent,
                highlightColor: Colors.transparent,
              ),
              child: LayoutWebCustom(
                menu: MenuBar(controllerRouteWeb: _controllerRouteWeb),
                content: StreamBuilder<String>(
                  initialData: RouteList.homeScreen,
                  stream: _controllerRouteWeb.stream,
                  builder: (context, snapshot) {
                    return Navigator(
                      key: Key(snapshot.data),
                      initialRoute: snapshot.data,
                      onGenerateRoute: (RouteSettings settings) {
                        return MaterialPageRoute(
                          builder: Routes.getRouteByName(settings.name),
                          settings: settings,
                          maintainState: false,
                          fullscreenDialog: true,
                        );
                      },
                    );
                  },
                ),
              )),
        ),
      );
    } else {
      final screenSize = MediaQuery.of(context).size;
      return Container(
        color: Theme.of(context).backgroundColor,
        child: Scaffold(
          backgroundColor: Theme.of(context).backgroundColor,
          resizeToAvoidBottomPadding: false,
          key: _scaffoldKey,
          body: WillPopScope(
            onWillPop: () async {
              if (tabController.index != 0) {
                tabController.animateTo(0);
                return false;
              } else {
                return showDialog(
                      context: context,
                      builder: (context) => AlertDialog(
                        title: Text(S.of(context).areYouSure),
                        content: Text(S.of(context).doYouWantToExitApp),
                        actions: <Widget>[
                          FlatButton(
                            onPressed: () => Navigator.of(context).pop(false),
                            child: Text(S.of(context).no),
                          ),
                          FlatButton(
                            onPressed: () => Navigator.of(context).pop(true),
                            child: Text(S.of(context).yes),
                          ),
                        ],
                      ),
                    ) ??
                    false;
              }
            },
            child: TabBarView(
              controller: tabController,
              physics: NeverScrollableScrollPhysics(),
              children: _tabView,
            ),
          ),
          drawer: Drawer(child: MenuBar()),
          bottomNavigationBar: SafeArea(
            top: false,
            child: Container(
              width: screenSize.width,
              child: FittedBox(
                child: Container(
                  width: screenSize.width /
                      (2 / (screenSize.height / screenSize.width)),
                  child: TabBar(
                    controller: tabController,
                    tabs: renderTabbar(),
                    isScrollable: false,
                    labelColor: Theme.of(context).primaryColor,
                    indicatorSize: TabBarIndicatorSize.label,
                    indicatorPadding: EdgeInsets.all(4.0),
                    indicatorColor: Theme.of(context).primaryColor,
                  ),
                ),
              ),
            ),
          ),
        ),
      );
    }
  }

  List<Widget> renderTabbar() {
    final isTablet = Tools.isTablet(MediaQuery.of(context));
    var totalCart = Provider.of<CartModel>(context).totalCartQuantity;
    final tabData = Provider.of<AppModel>(context, listen: false)
        .appConfig['TabBar'] as List;

    List<Widget> list = [];

    tabData.forEach((item) {
      var icon = !item["icon"].contains('/')
          ? Icon(
              featherIcons[item["icon"]],
              color: Theme.of(context).accentColor,
              size: 22,
            )
          : (item["icon"].contains('http')
              ? Image.network(
                  item["icon"],
                  color: Theme.of(context).accentColor,
                  width: 24,
                )
              : Image.asset(
                  item["icon"],
                  color: Theme.of(context).accentColor,
                  width: 24,
                ));

      if (item["layout"] == "cart") {
        icon = Stack(
          children: <Widget>[
            Container(
              width: 30,
              height: 25,
              padding: const EdgeInsets.only(right: 6.0, top: 4),
              child: icon,
            ),
            if (totalCart > 0)
              Positioned(
                right: 0,
                top: 0,
                child: Container(
                  padding: const EdgeInsets.all(1),
                  decoration: BoxDecoration(
                    color: Colors.red,
                    borderRadius: BorderRadius.circular(8),
                  ),
                  constraints: BoxConstraints(
                    minWidth: 16,
                    minHeight: 16,
                  ),
                  child: Text(
                    totalCart.toString(),
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: isTablet ? 14 : 12,
                    ),
                    textAlign: TextAlign.center,
                  ),
                ),
              )
          ],
        );
      }

      if (item["label"] != null) {
        list.add(Tab(icon: icon, text: item["label"]));
      } else {
        list.add(Tab(icon: icon));
      }
    });

    return list;
  }
}

这里是 BannerImageItem

/// The Banner type to display the image
class BannerImageItem extends StatefulWidget {
  @override
  final Key key;
  final dynamic config;
  final double width;
  final double padding;
  final BoxFit boxFit;
  final double radius;

  BannerImageItem({
    this.key,
    this.config,
    this.padding,
    this.width,
    this.boxFit,
    this.radius,
  }) : super(key: key);

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

class _BannerImageItemState extends State<BannerImageItem>
    with AfterLayoutMixin {
  List<Product> _products;

  GlobalKey<MainTabsState> _scaffoldKey = GlobalKey<MainTabsState>();

  @override
  void afterFirstLayout(BuildContext context) {
    /// for pre-load the list product
    if (widget.config['data'] != null) {
      print(widget.config['data']);
      _products = widget.config['data'];
    }
  }

  @override
  Widget build(BuildContext context) {
    double _padding =
        Tools.formatDouble(widget.config["padding"] ?? widget.padding ?? 10.0);
    double _radius = Tools.formatDouble(widget.config['radius'] ??
        (widget.radius != null ? widget.radius : 0.0));

    final screenSize = MediaQuery.of(context).size;
    final screenWidth =
        screenSize.width / (2 / (screenSize.height / screenSize.width));
    final itemWidth = widget.width ?? screenWidth;

    return GestureDetector(key: _scaffoldKey,
      onTap: () { _scaffoldKey.currentState.tabController.animateTo(1);
      },
      child: Container(
        width: itemWidth,
        child: Padding(
          padding: EdgeInsets.only(left: _padding, right: _padding),
          child: ClipRRect(
            borderRadius: BorderRadius.circular(_radius),
            child: Tools.image(
              fit: widget.boxFit ?? BoxFit.fitWidth,
              url: widget.config["image"],
            ),
          ),
        ),
      ),
    );
  }
}
return GestureDetector(key: _scaffoldKey,
      onTap: () { _scaffoldKey.currentState.tabController.animateTo(1);
      },
      child: Container(
        width: itemWidth,
        child: Padding(
          padding: EdgeInsets.only(left: _padding, right: _padding),
          child: ClipRRect(
            borderRadius: BorderRadius.circular(_radius),
            child: Tools.image(
              fit: widget.boxFit ?? BoxFit.fitWidth,
              url: widget.config["image"],
            ),
          ),
        ),
      ),
    );

替换为

return GestureDetector(
      onTap: () { MainTabControlDelegate.getInstance().tabAnimateTo(1);
      },
      child: Container(
        width: itemWidth,
        child: Padding(
          padding: EdgeInsets.only(left: _padding, right: _padding),
          child: ClipRRect(
            borderRadius: BorderRadius.circular(_radius),
            child: Tools.image(
              fit: widget.boxFit ?? BoxFit.fitWidth,
              url: widget.config["image"],
            ),
          ),
        ),
      ),
    );