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

Flutter ARCore Plugin二次进入崩溃及Overlay无法关闭问题排查

增强图像识别APP问题排查与修复

问题清单

  • AR页面首次访问可正常扫描图像获取结果,但退出后再次进入崩溃
  • 点击Overlay关闭按钮后,Overlay无法消失

报错核心信息

E/AndroidRuntime( 7710): FATAL EXCEPTION: main
E/AndroidRuntime( 7710): java.lang.RuntimeException: Unable to resume activity 
{com.example.shopping_mall_app/com.example.shopping_mall_app.MainActivity}: 
java.lang.NullPointerException: null cannot be cast to non-null type android.view.View
E/AndroidRuntime( 7710): Caused by: java.lang.NullPointerException: null cannot be cast to non-null type android.view.View
E/AndroidRuntime( 7710):  at com.difrancescogianmarco.arcore_flutter_plugin.BaseArCoreView.getView(BaseArCoreView.kt:88)

问题分析

  1. 崩溃原因:

    • ARCoreController未彻底释放,退出页面后残留会话状态导致再次进入时重复初始化,触发空指针异常
    • 页面嵌套MaterialApp引发路由管理混乱,退出时未彻底清理AR视图资源
    • dispose方法未清空controller的回调监听,后台仍有状态更新尝试
  2. Overlay无法关闭原因:

    • _handleOnTrackingImage方法中,每次追踪到图像都会无条件调用setState(() => showOverlay = true),直接覆盖用户的关闭操作

修复代码

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:arcore_flutter_plugin/arcore_flutter_plugin.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:shopping_mall_app/app/pages/main_menu.dart';

class ARScan extends StatefulWidget {
  @override
  _ARScan createState() => _ARScan();
}

class _ARScan extends State<ARScan> {
  ArCoreController? arCoreController;
  Map<int, ArCoreAugmentedImage> augmentedImagesMap = {};
  bool showOverlay = false;
  List<Map<String, dynamic>> productList = [];
  bool _isDisposed = false; // 标记页面是否已销毁

  @override
  Widget build(BuildContext context) {
    // 移除嵌套MaterialApp,直接返回Scaffold(上层已有MaterialApp)
    return Scaffold(
      appBar: AppBar(
        title: const Text('AugmentedPage'),
        leading: IconButton(
          icon: const Icon(Icons.arrow_back),
          onPressed: () async {
            // 退出前提前释放AR资源
            await arCoreController?.dispose();
            Navigator.pushAndRemoveUntil(
              context,
              MaterialPageRoute(builder: (context) => MainMenu()),
              (route) => false,
            );
          },
        ),
      ),
      body: Stack(
        children: [
          ArCoreView(
            onArCoreViewCreated: _onArCoreViewCreated,
            type: ArCoreViewType.AUGMENTEDIMAGES,
          ),
          if (showOverlay) _buildOverlay(),
        ],
      ),
    );
  }

  Widget _buildOverlay() {
    return Positioned(
      bottom: 20,
      left: 20,
      right: 20,
      child: Material(
        color: Colors.white.withOpacity(0.8),
        borderRadius: BorderRadius.circular(10),
        child: Padding(
          padding: const EdgeInsets.all(10.0),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  const Text(
                    'Detected Deals!',
                    style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                  ),
                  IconButton(
                    icon: const Icon(Icons.close),
                    onPressed: () {
                      setState(() {
                        showOverlay = false;
                      });
                    },
                  ),
                ],
              ),
              const SizedBox(height: 10),
              ...productList.map((product) => ListTile(
                leading: product['ImageURL'] != null
                    ? Image.network(product['ImageURL'], width: 50)
                    : const Icon(Icons.image),
                title: Text(product['Product Name'] ?? 'Unnamed Product'),
                subtitle: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('Cost: ${product['Enter Product Cost'] ?? 'N/A'}'),
                    Text('Discount: ${product['Enter Discount'] ?? 'N/A'}'),
                    Text('Points: ${product['Enter Points'] ?? 'N/A'}'),
                  ],
                ),
              )),
            ],
          ),
        ),
      ),
    );
  }

  void _onArCoreViewCreated(ArCoreController controller) async {
    if (_isDisposed) return; // 页面已销毁则跳过初始化
    arCoreController = controller;
    arCoreController?.onTrackingImage = _handleOnTrackingImage;
    await loadSingleImage();
    await fetchProductsFromFirebase();
  }

  Future<void> loadSingleImage() async {
    if (_isDisposed || arCoreController == null) return;
    final ByteData bytes = await rootBundle.load('Assets/testings.jpg');
    await arCoreController?.loadSingleAugmentedImage(
      bytes: bytes.buffer.asUint8List(),
    );
  }

  Future<void> fetchProductsFromFirebase() async {
    final querySnapshot = await FirebaseFirestore.instance
        .collection('users')
        .doc('7r6AWJR9HlaHZQ05GE4bmKtkjE13')
        .collection('products')
        .get();

    if (!_isDisposed) { // 页面未销毁才更新状态
      setState(() {
        productList = querySnapshot.docs.map((doc) => doc.data()).toList();
      });
    }
  }

  void _handleOnTrackingImage(ArCoreAugmentedImage augmentedImage) {
    if (_isDisposed) return;
    if (!augmentedImagesMap.containsKey(augmentedImage.index)) {
      augmentedImagesMap[augmentedImage.index] = augmentedImage;
    }
    // 仅当Overlay未显示时才打开,避免覆盖用户关闭操作
    if (!showOverlay) {
      setState(() {
        showOverlay = true;
      });
    }
  }

  @override
  void dispose() {
    _isDisposed = true; // 标记页面已销毁
    arCoreController?.onTrackingImage = null; // 清空回调监听
    arCoreController?.dispose(); // 释放AR资源
    super.dispose();
  }
}

关键修改点

  1. 修复崩溃问题:

    • 移除嵌套的MaterialApp,解决路由管理冲突
    • 新增_isDisposed标志位,防止页面销毁后执行无效操作
    • 退出页面时提前调用arCoreController?.dispose(),彻底清理AR会话
    • 在dispose方法中清空onTrackingImage回调,避免后台触发状态更新
  2. 修复Overlay无法关闭问题:

    • 修改_handleOnTrackingImage方法,仅当showOverlay为false时才设置为true,不再覆盖用户的关闭操作

内容的提问来源于stack exchange,提问作者srbjeet singh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 23:14:53