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

Flutter无第三方包实现隐私屏幕问题求助

Flutter 后台隐私模糊屏实现方案

问题背景

我正尝试为Flutter应用实现后台隐私模糊屏,避免用户切换应用或应用进入后台时快照可见。尝试了多种方案但未成功,且希望不使用任何第三方包。以下是基于@Duy Tran反馈修改后的代码:

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:pinvalut/Add%20Bank/add_bank.dart';
import 'package:pinvalut/SetupAppLock/setup_app_lock.dart';
import 'package:pinvalut/Utils/app_settings.dart';
import 'package:pinvalut/Utils/screen_blocker.dart';
import 'package:pinvalut/login.dart';
import 'package:provider/provider.dart';
import 'package:pinvalut/Homescreen/homescreen.dart';
import 'package:pinvalut/Theme/theme.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );

  final settings = await loadSettings();

  runApp(MyApp(initialSettings: settings));
}

class MyApp extends StatefulWidget {
  final AppSettings initialSettings;

  const MyApp({super.key, required this.initialSettings});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  late ThemeProvider themeProvider;
  bool _isInBackground = false;
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    themeProvider =
        ThemeProvider(isDarkMode: widget.initialSettings.darkMode ?? false);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.inactive) {
      print('App in background>>');
      setState(() {
        _isInBackground = true;
        //  print('App in background>>${_isInBackground}');
      });
    } else if (state == AppLifecycleState.resumed) {
      print('App in foreground>>');
      setState(() {
        _isInBackground = false;
        //  print('App in foreground>>${_isInBackground}');
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider.value(
      value: themeProvider,
      child: Consumer<ThemeProvider>(
        builder: (context, themeProvider, _) {
          if (_isInBackground) {
            print('APP IS >> ${_isInBackground}');
            return MaterialApp(
              debugShowCheckedModeBanner: false,
              home: Stack(
                  alignment: Alignment.center,
                  fit: StackFit.expand,
                  textDirection: TextDirection.ltr,
                  children: [
                    Positioned.fill(
                      child: BackdropFilter(
                        filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
                        child: Container(
                          color: const Color.fromARGB(255, 182, 175, 175)
                              .withOpacity(0.5),
                          child: const Center(
                            child: Text(
                              'App in Background',
                              style: TextStyle(
                                  fontSize: 24, fontWeight: FontWeight.bold),
                            ),
                          ),
                        ),
                      ),
                    )
                  ]),
            );
          } else {
            return MaterialApp(
              debugShowCheckedModeBanner: false,
              initialRoute: '/',
              routes: {
                '/': (context) => const HomeScreen(),
                '/login': (context) => const LoginPage(),
                '/home': (context) => const HomeScreen(),
                '/add-bank': (context) => const AddBank(bankName: ''),
                '/setup_app_lock': (context) => const SetupAppLock()
              },
              theme: themeProvider.isDarkMode
                  ? AppTheme.darkTheme
                  : AppTheme.lightTheme,
            );
          }
        },
      ),
    );
  }
}

问题分析

当前代码的核心问题是应用进入后台时直接替换了整个MaterialApp,这会导致:

  • 应用原有状态丢失
  • 系统快照可能在setState更新前就已生成,模糊层无法生效

另外,仅监听inactive状态不够,iOS的后台快照是在paused状态下生成的,需要同时处理该状态。

修正方案

正确的做法是在现有App结构之上叠加模糊层,而非替换整个App。以下是修正后的完整代码:

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:pinvalut/Add%20Bank/add_bank.dart';
import 'package:pinvalut/SetupAppLock/setup_app_lock.dart';
import 'package:pinvalut/Utils/app_settings.dart';
import 'package:pinvalut/Utils/screen_blocker.dart';
import 'package:pinvalut/login.dart';
import 'package:provider/provider.dart';
import 'package:pinvalut/Homescreen/homescreen.dart';
import 'package:pinvalut/Theme/theme.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );

  final settings = await loadSettings();

  runApp(MyApp(initialSettings: settings));
}

class MyApp extends StatefulWidget {
  final AppSettings initialSettings;

  const MyApp({super.key, required this.initialSettings});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  late ThemeProvider themeProvider;
  bool _isInBackground = false;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    themeProvider = ThemeProvider(isDarkMode: widget.initialSettings.darkMode ?? false);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    setState(() {
      // 同时监听inactive和paused状态,覆盖iOS和Android的后台场景
      _isInBackground = state == AppLifecycleState.inactive || state == AppLifecycleState.paused;
    });
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider.value(
      value: themeProvider,
      child: Consumer<ThemeProvider>(
        builder: (context, themeProvider, _) {
          return MaterialApp(
            debugShowCheckedModeBanner: false,
            theme: themeProvider.isDarkMode ? AppTheme.darkTheme : AppTheme.lightTheme,
            // 在整个App之上叠加模糊层
            home: Stack(
              fit: StackFit.expand,
              children: [
                // 原有App内容
                Navigator(
                  initialRoute: '/',
                  onGenerateRoute: (settings) {
                    final routeName = settings.name;
                    final Map<String, Widget Function(BuildContext)> routes = {
                      '/': (context) => const HomeScreen(),
                      '/login': (context) => const LoginPage(),
                      '/home': (context) => const HomeScreen(),
                      '/add-bank': (context) => const AddBank(bankName: ''),
                      '/setup_app_lock': (context) => const SetupAppLock()
                    };
                    final routeBuilder = routes[routeName];
                    if (routeBuilder != null) {
                      return MaterialPageRoute(builder: routeBuilder);
                    }
                    return MaterialPageRoute(builder: (_) => const HomeScreen());
                  },
                ),
                // 后台模糊层,仅在后台显示
                if (_isInBackground)
                  Positioned.fill(
                    child: BackdropFilter(
                      filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
                      child: Container(
                        color: themeProvider.isDarkMode 
                            ? Colors.black.withOpacity(0.7) 
                            : Colors.white.withOpacity(0.7),
                        child: Center(
                          child: Text(
                            '隐私保护中',
                            style: TextStyle(
                              fontSize: 24,
                              fontWeight: FontWeight.bold,
                              color: themeProvider.isDarkMode ? Colors.white : Colors.black,
                            ),
                          ),
                        ),
                      ),
                    ),
                  ),
              ],
            ),
          );
        },
      ),
    );
  }
}

关键改动说明

  1. 保留原有App结构:不再切换整个MaterialApp,而是在Navigator外层叠加模糊层,确保应用状态不丢失。
  2. 完善生命周期监听:同时监听inactive和paused状态,适配iOS和Android的后台快照时机。
  3. 主题适配:模糊层颜色跟随当前主题,保持视觉一致性。
  4. 提升模糊效果:将sigma值调整为10,增强隐私保护力度。

内容的提问来源于stack exchange,提问作者Arun-

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 03:03:14