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

Flutter切换扫描标签页时mobile_scanner相机不显示问题求助

Flutter切换标签页时相机无法显示的问题解决

问题现象

  • 项目基于mobile_scanner包实现两个扫码场景:Scan1使用barcode_scanner_window.dart,Scan2使用barcode_scanner_simple.dart,通过底部导航栏切换页面
  • 从Home页切换到任意扫码页时,相机可正常显示
  • 从一个扫码页直接切换到另一个扫码页时,目标页面相机无法显示(如Scan1→Scan2时Scan2黑屏,Home→Scan2→Scan1时Scan1黑屏)

项目代码

main.dart

import 'package:flutter/material.dart';
import 'package:mobile_scanner_bug/business.dart';
import 'package:mobile_scanner_bug/home.dart';
import 'package:mobile_scanner_bug/school.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Tab Bar Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _selectedIndex = 0;

  static const List<Widget> _widgetOptions = <Widget>[
    Home(),
    School(),
    Business(),
    
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Tab Bar'),
      ),
      body: Center(
        child: _widgetOptions.elementAt(_selectedIndex),
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.camera),
            label: 'Scan1',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.camera_alt),
            label: 'Scan2',
          ),
        ],
        currentIndex: _selectedIndex,
        selectedItemColor: Colors.amber[800],
        onTap: _onItemTapped,
      ),
    );
  }
}

school.dart(对应Scan1)

import 'package:flutter/material.dart';
import 'package:mobile_scanner_bug/barcode_scanner_window.dart';

class School extends StatefulWidget {
  const School({super.key});

  @override
  State<School> createState() => _SchoolState();
}

class _SchoolState extends State<School> {
  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        BarcodeScannerWithScanWindow(),
        Center(child: Text('Scan1')),
      ],
    );
  }
}

business.dart(对应Scan2)

import 'package:flutter/material.dart';
import 'package:mobile_scanner_bug/barcode_scanner_simple.dart';

class Business extends StatefulWidget {
  const Business({super.key});

  @override
  State<Business> createState() => _BusinessState();
}

class _BusinessState extends State<Business> {
  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        BarcodeScannerSimple(),
        const Center(child: Text('Scan2')),
      ],
    );
  }
}

home.dart

import 'package:flutter/material.dart';

class Home extends StatefulWidget {
  const Home({super.key});

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  @override
  Widget build(BuildContext context) {
    return Center(child: Text('Home'));
  }
}

问题原因及解决方法

原因

Flutter底部导航栏默认会缓存所有页面状态,两个扫码页同时存在于Widget树中时,mobile_scanner的相机资源会被先加载的页面占用,导致后切换的页面无法获取相机权限/资源,出现黑屏。

解决方法

方法1:控制相机生命周期(推荐,保留页面状态)

通过GlobalKey获取扫码页状态,切换标签时手动启停相机:

  1. 修改main.dart中的_MyHomePageState:
class _MyHomePageState extends State<MyHomePage> {
  int _selectedIndex = 0;
  final GlobalKey<_SchoolState> _schoolKey = GlobalKey();
  final GlobalKey<_BusinessState> _businessKey = GlobalKey();

  void _onItemTapped(int index) {
    setState(() {
      // 切换前停止当前扫码页的相机
      if (_selectedIndex == 1) {
        _schoolKey.currentState?.stopCamera();
      } else if (_selectedIndex == 2) {
        _businessKey.currentState?.stopCamera();
      }
      _selectedIndex = index;
      // 切换后启动目标扫码页的相机
      if (_selectedIndex == 1) {
        _schoolKey.currentState?.startCamera();
      } else if (_selectedIndex == 2) {
        _businessKey.currentState?.startCamera();
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Tab Bar'),
      ),
      body: IndexedStack(
        index: _selectedIndex,
        children: [
          const Home(),
          School(key: _schoolKey),
          Business(key: _businessKey),
        ],
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.camera),
            label: 'Scan1',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.camera_alt),
            label: 'Scan2',
          ),
        ],
        currentIndex: _selectedIndex,
        selectedItemColor: Colors.amber[800],
        onTap: _onItemTapped,
      ),
    );
  }
}
  1. 修改school.dart添加相机控制方法:
import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';

class School extends StatefulWidget {
  const School({super.key});

  @override
  State<School> createState() => _SchoolState();
}

class _SchoolState extends State<School> {
  late MobileScannerController _controller;

  @override
  void initState() {
    super.initState();
    _controller = MobileScannerController();
  }

  void startCamera() {
    _controller.start();
  }

  void stopCamera() {
    _controller.stop();
  }

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

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        MobileScanner(controller: _controller),
        Center(child: Text('Scan1')),
      ],
    );
  }
}
  1. 同理修改business.dart:
import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';

class Business extends StatefulWidget {
  const Business({super.key});

  @override
  State<Business> createState() => _BusinessState();
}

class _BusinessState extends State<Business> {
  late MobileScannerController _controller;

  @override
  void initState() {
    super.initState();
    _controller = MobileScannerController();
  }

  void startCamera() {
    _controller.start();
  }

  void stopCamera() {
    _controller.stop();
  }

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

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        MobileScanner(controller: _controller),
        const Center(child: Text('Scan2')),
      ],
    );
  }
}

方法2:动态重建页面(简单但无状态缓存)

修改main.dart,每次切换时动态构建页面,避免多个扫码页同时存在:

class _MyHomePageState extends State<MyHomePage> {
  int _selectedIndex = 0;

  Widget _getCurrentPage() {
    switch(_selectedIndex) {
      case 0: return const Home();
      case 1: return const School();
      case 2: return const Business();
      default: return const Home();
    }
  }

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Tab Bar'),
      ),
      body: Center(
        child: _getCurrentPage(),
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.camera),
            label: 'Scan1',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.camera_alt),
            label: 'Scan2',
          ),
        ],
        currentIndex: _selectedIndex,
        selectedItemColor: Colors.amber[800],
        onTap: _onItemTapped,
      ),
    );
  }
}

内容的提问来源于stack exchange,提问作者Muhammad Taimoor

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 20:04:52