You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中如何点击按钮切换NavigationBar的当前页面?

实现Flutter导航栏标签页与弹窗按钮的页面切换

我有一个包含3个标签页的NavigationBar,需求是点击弹窗中的按钮时,切换到指定的标签页(购物车页面,即第三个标签页)。以下是相关代码:

Home类导航栏代码

body: IndexedStack(
  index: currentPageIndex,
  children: <Widget>[
    const ShopPage(),
    QRScanner(showTab2: () {
      setState(() {
        setState(() {
          currentPageIndex = 2;
        });
      });
    }),
    const ShopListPage(),
  ],
),    
bottomNavigationBar: NavigationBar(
  elevation: 2,
  labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected,
  selectedIndex: currentPageIndex,
  onDestinationSelected: (int index) {
    setState(() => currentPageIndex = index);
  },
  destinations: const [
    NavigationDestination(
      icon: Icon(Icons.store_outlined),
      selectedIcon: Icon(Icons.store),
      label: 'Tienda',
    ),
    NavigationDestination(
      icon: Icon(Icons.qr_code_scanner_outlined),
      selectedIcon: Icon(Icons.qr_code_scanner),
      label: 'Escaner',
    ),
    NavigationDestination(
      icon: Icon(Icons.shopping_cart_outlined),
      selectedIcon: Icon(Icons.shopping_cart),
      label: 'Carrito',
    ),
  ],
),

QRScanner页面弹窗函数代码

void onQRViewCreated(QRViewController controller) {
  setState(() {
    this.controller = controller;
  });
  controller.scannedDataStream.listen((scanData) {
    setState(() {
      result = scanData;
    });
    controller.pauseCamera();
    Stream<List<Products>> readProducts() => FirebaseFirestore.instance
        .collection('products')
        .where('id', isEqualTo: '${result!.code}')
        .snapshots()
        .map((snapshot) => snapshot.docs
            .map((doc) => Products.fromJson(doc.data()))
            .toList());

    Future<bool> onWillPop() async {
      return false;
    }

    showDialog(
      barrierDismissible: false,
      context: context,
      builder: (BuildContext context) => WillPopScope(
        onWillPop: () async => false,
        child: WillPopScope(
          onWillPop: onWillPop,
          child: Dialog(
            insetPadding: const EdgeInsets.all(8),
            child: StreamBuilder<List<Products>>(
                stream: readProducts(),
                builder: (context, snapshot) {
                  if (snapshot.hasError) {
                    return Text('Algo ha ocurrido! ${snapshot.error}');
                  } else if (snapshot.hasData) {
                    final products = snapshot.data!;
                    return Container(
                      margin: const EdgeInsets.all(8),
                      child: Column(
                          mainAxisSize: MainAxisSize.min,
                          children: products
                              .map((p) => BuildQRCards( // 此类包含需触发页面切换的按钮
                                  products: p, controller: controller,))
                              .toList()),
                    );
                  } else {
                    return const Center(child: CircularProgressIndicator());
                  }
                }),
          ),
        ),
      ),
    );
  });
}

BuildQRCard类代码

class _BuildQRCardsState extends State<BuildQRCards> {
  @override
  Widget build(BuildContext context) {
    return Card(
      clipBehavior: Clip.antiAlias,
      child: SizedBox(
        width: 300,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisSize: MainAxisSize.min,
          children: [
            Image(image: NetworkImage(widget.products.imageUrl)),
            ListTile(
              title: Text(widget.products.name),
              subtitle: Text('Precio: ${widget.products.price}\$'),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                Padding(
                  padding: const EdgeInsets.fromLTRB(0, 0, 8, 8),
                  child: ElevatedButton(
                    style: ButtonStyle(
                      foregroundColor: MaterialStateProperty.all<Color>(
                        const Color(0xFFFFFFFF),
                      ),
                      backgroundColor: MaterialStateProperty.all<Color>(
                        const Color(0xFF6750A4),
                      ),
                    ),
                    onPressed: () {
                      Navigator.pop(context);
                      widget.controller!.resumeCamera();
                    },
                    child: const Text(
                      'Cancelar',
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.fromLTRB(0, 0, 8, 8),
                  child: ElevatedButton(
                    style: ButtonStyle(
                      foregroundColor: MaterialStateProperty.all<Color>(
                        const Color(0xFFFFFFFF),
                      ),
                      backgroundColor: MaterialStateProperty.all<Color>(
                        const Color(0xFF6750A4),
                      ),
                    ),
                    onPressed: () => showDialog(
                      context: context,
                      barrierDismissible: false,
                      builder: (BuildContext context) => AlertDialog(
                        content: Text(
                          '¿Desea añadir el producto ${widget.products.name} a la lista de compras?',
                          textAlign: TextAlign.center,
                        ),
                        actions: <Widget>[
                          TextButton(
                              onPressed: () {
                                Navigator.pop(context);
                              },
                              child: const Text('Cancelar')),
                          TextButton(
                            onPressed: () {
                              passID(widget.products.id);
                              widget.controller!.resumeCamera();
                            },
                            child: const Text('Añadir'),
                          ),
                        ],
                      ),
                    ),
                    child: const Text(
                      'Añadir al Carro',
                    ),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

解决方案

通过回调函数传递实现弹窗按钮触发标签页切换,步骤如下:

  1. 更新Home类中QRScanner的初始化
    添加切换到购物车的回调,简化原有的嵌套setState:

    QRScanner(
      showTab2: () {
        setState(() {
          currentPageIndex = 2;
        });
      },
      // 新增切换到购物车的回调
      switchToCart: () {
        setState(() {
          currentPageIndex = 2;
        });
      },
    ),
    
  2. 修改QRScanner类的定义
    添加switchToCart回调参数,并传递给BuildQRCards:

    class QRScanner extends StatefulWidget {
      final VoidCallback showTab2;
      final VoidCallback switchToCart; // 新增参数
    
      const QRScanner({super.key, required this.showTab2, required this.switchToCart});
    
      @override
      State<QRScanner> createState() => _QRScannerState();
    }
    

    在创建BuildQRCards时传递回调:

    BuildQRCards(
      products: p,
      controller: controller,
      switchToCart: widget.switchToCart, // 传递回调
    )
    
  3. 修改BuildQRCards类的定义
    添加switchToCart回调参数,并在确认添加产品时触发:

    class BuildQRCards extends StatefulWidget {
      final Products products;
      final QRViewController? controller;
      final VoidCallback switchToCart; // 新增参数
    
      const BuildQRCards({super.key, required this.products, this.controller, required this.switchToCart});
    
      @override
      State<BuildQRCards> createState() => _BuildQRCardsState();
    }
    
  4. 更新确认按钮的逻辑
    在"Añadir"按钮的onPressed中,关闭所有弹窗并触发标签页切换:

    TextButton(
      onPressed: () {
        passID(widget.products.id);
        widget.controller!.resumeCamera();
        // 关闭确认弹窗
        Navigator.pop(context);
        // 关闭产品详情弹窗
        Navigator.pop(context);
        // 触发切换到购物车标签页
        widget.switchToCart();
      },
      child: const Text('Añadir'),
    ),
    

内容的提问来源于stack exchange,提问作者Willders Carvajal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 16:00:20