flutter开发笔记

1、语法

1.1 空值赋值运算符

复制代码
_fToast ??= FToast();

if (_fToast == null) {
  _fToast = FToast();
}

1.2 定时器

依赖import 'dart:async';

复制代码
import 'dart:async';
import 'package:flutter/material.dart';

class TestPage extends StatefulWidget {
  @override
  _TestPageState createState() => _TestPageState();
}

class _TestPageState extends State<TestPage> {
  late Timer timer;

  @override
  void initState() {
    super.initState();

    // 1. 延迟执行(setTimeout)
    Future.delayed(Duration(seconds: 2), () {
      print("2秒后执行");
    });

    // 2. 循环执行(setInterval)
    timer = Timer.periodic(Duration(seconds: 1), (timer) {
      print("每秒执行一次");
    });
  }

  @override
  void dispose() {
    timer.cancel(); // 页面销毁时必须停止!防内存泄漏
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(body: Center(child: Text("定时器测试")));
  }
}

延时器还可以如下写

复制代码
// 1. 如果定时器正在运行,先取消(防止重复触发)
_logoutDebounceTimer?.cancel();

// 2. 创建一个【3秒后只执行1次】的定时器
_logoutDebounceTimer = Timer(const Duration(seconds: 3), () {
  _isLoggingOut = false; // 3秒后执行这句
});

1.3 WidgetsBinding.instance

核心方法

复制代码
// 1. 帧后执行(如获取尺寸、状态同步)
WidgetsBinding.instance.addPostFrameCallback((_) {
  // 代码在当前帧绘制完成后执行
});

// 2. 监听应用生命周期
class MyObserver with WidgetsBindingObserver {
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    print('生命周期: $state');
  }
}
WidgetsBinding.instance.addObserver(MyObserver());

// 3. 等待首帧完成
await WidgetsBinding.instance.endOfFrame;

1. 帧调度 / 回调

  • addPostFrameCallback (FrameCallback callback) → void:添加帧后回调(当前帧结束执行)。
  • addPersistentFrameCallback (FrameCallback callback) → void:添加持久帧回调(每帧执行)Flutter。
  • scheduleFrame () → void:主动调度一帧。
  • scheduleForcedFrame () → void:强制调度帧(无视 Vsync)Flutter。
  • cancelFrameCallbackWithId (int id) → void:取消指定 ID 的帧回调。

2. 生命周期与监听

  • addObserver (WidgetsBindingObserver observer) → void:添加生命周期 / 路由监听。
  • removeObserver (WidgetsBindingObserver observer) → void:移除监听。

3. 任务调度

  • scheduleTask (VoidCallback task, Priority priority) → void:调度高 / 中 / 低优先级任务。

4. 根 Widget 管理

  • scheduleAttachRootWidget (Widget rootWidget) → void:调度挂载根 WidgetFlutter。
  • attachRootWidget (Widget rootWidget) → void:直接挂载根 Widget。

5. 服务与扩展

  • registerBoolServiceExtension (...) → void:注册布尔型服务扩展(调试用)。
  • setSemanticsEnabled (bool enabled) → void:开启 / 关闭语义化(无障碍)。

6. 常用工具

  • ensureVisualUpdate () → void:确保视图更新(触发重绘)。
  • handleAppLifecycleStateChanged (AppLifecycleState state) → void:手动触发生命周期变更。

核心属性

1. 调度 / 帧相关

  • endOfFrameFuture<void>:当前帧结束后完成的 Future,常用于帧后执行代码Flutter。
Dart 复制代码
// 等待当前帧结束
await WidgetsBinding.instance.endOfFrame;
print('当前帧已结束');
  • currentFrameTimeStampDuration:当前帧时间戳Flutter。
Dart 复制代码
final timeStamp = WidgetsBinding.instance.currentFrameTimeStamp;
print('当前帧时间戳(微秒): $timeStamp'); // 0:00:00.123456  0 小时 0 分 0 秒 123456 微秒

// 1. 总微秒数(最精确,Flutter 内部用这个)
int microseconds = timeStamp.inMicroseconds;

// 2. 总毫秒数
int ms = timeStamp.inMilliseconds;

// 3. 总秒数
int seconds = timeStamp.inSeconds;
  • schedulerPhaseSchedulerPhase:当前调度阶段(idle、transientCallbacks、midFrameMicrotasks、persistentCallbacks、postFrameCallbacks)Flutter。

一帧完整执行流程

idle → transientCallbacks → midFrameMicrotasks → persistentCallbacks → postFrameCallbacks → idle

Flutter SchedulerPhase 5 个阶段完整对照表(清晰版)

表格

阶段名称 英文原值 执行顺序 核心作用 对应开发者操作 注意事项
空闲 idle 0 无帧任务,等待下一次 VSync 信号 正常待机状态
动画回调 transientCallbacks 1 执行动画、Ticker 回调,更新动画值 AnimationController 驱动 不要在这里做耗时操作
帧中微任务 midFrameMicrotasks 2 清空上一阶段产生的微任务(microtask) 开发者一般无感 系统内部清理
构建布局绘制 persistentCallbacks 3 build → layout → paint 真正渲染 UI setState 触发更新 最核心阶段,禁止阻塞
帧后回调 postFrameCallbacks 4 执行帧结束后的回调 addPostFrameCallback 适合获取尺寸、执行收尾
Dart 复制代码
import 'package:flutter/scheduler.dart';  
final phase = WidgetsBinding.instance.schedulerPhase;
print('当前调度阶段: $phase');

// 可判断阶段
if (phase == SchedulerPhase.idle) {
  print('空闲');
}
  • hasScheduledFramebool:是否已调度下一帧Flutter。
Dart 复制代码
final scheduled = WidgetsBinding.instance.hasScheduledFrame;
print('是否已调度下一帧: $scheduled');
  • framesEnabledbool:是否允许调度帧。
Dart 复制代码
final enabled = WidgetsBinding.instance.framesEnabled;
print('帧是否启用: $enabled');

2. 应用生命周期

  • lifecycleStateAppLifecycleState?:应用生命周期(resumed、inactive、paused、detached、hidden)Flutter。

Flutter 所有 5 种生命周期说明

表格

状态 含义
resumed 应用在前台,可交互
inactive 应用在前台,但不活跃(来电、弹窗、切换应用过渡)
paused 应用在后台,不可见
hidden 应用完全隐藏
detached 应用正在销毁 / 退出
Dart 复制代码
final state = WidgetsBinding.instance.lifecycleState;
print('当前应用状态: $state');

if (state == AppLifecycleState.resumed) {
  print('应用在前台');
}
Dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const LifecyclePage(),
    );
  }
}

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

  @override
  State<LifecyclePage> createState() => _LifecyclePageState();
}

class _LifecyclePageState extends State<LifecyclePage> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    // 注册生命周期监听
    WidgetsBinding.instance.addObserver(this);
    
    // 你要的:立即获取当前状态
    _getCurrentAppState();
  }

  // 获取当前应用状态
  void _getCurrentAppState() {
    final state = WidgetsBinding.instance.lifecycleState;
    print('当前应用状态: $state');

    if (state == AppLifecycleState.resumed) {
      print('应用在前台(活跃)');
    } else if (state == AppLifecycleState.inactive) {
      print('应用在前台(不活跃,如来电、弹窗)');
    } else if (state == AppLifecycleState.paused) {
      print('应用在后台(不可见)');
    } else if (state == AppLifecycleState.detached) {
      print('应用正在销毁');
    } else if (state == AppLifecycleState.hidden) {
      print('应用完全隐藏');
    }
  }

  // 监听生命周期变化(所有状态都会触发)
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    print('\n===== 应用状态变化 =====');
    print('新状态: $state');

    switch (state) {
      case AppLifecycleState.resumed:
        print('✅ 应用在前台(活跃、可交互)');
        break;
      case AppLifecycleState.inactive:
        print('⚠️ 应用在前台(不活跃,如弹窗、来电)');
        break;
      case AppLifecycleState.paused:
        print('⏸️ 应用在后台(不可见)');
        break;
      case AppLifecycleState.hidden:
        print('👁️ 应用完全隐藏');
        break;
      case AppLifecycleState.detached:
        print('❌ 应用正在销毁(退出)');
        break;
      default:
        print('❓ 未知状态');
    }
  }

  @override
  void dispose() {
    // 移除监听
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Flutter 完整生命周期')),
      body: const Center(child: Text('查看控制台输出生命周期')),
    );
  }
}

3. 渲染 / 视图

  • renderViewRenderView:根渲染对象。整个应用的「根渲染画布」
  • pipelineOwnerPipelineOwner:渲染管线管理者。渲染流程的「总调度员」
  • buildOwnerBuildOwner:Widget 构建管理者。Widget 树的「构建总指挥」
Dart 复制代码
final renderView = WidgetsBinding.instance.renderView;
print('根渲染对象 size: ${renderView.size}');  
final pipeline = WidgetsBinding.instance.pipelineOwner;
print('渲染管线 owner: $pipeline');  
final buildOwner = WidgetsBinding.instance.buildOwner;
print('构建 owner: $buildOwner');  
对象 核心职责 对应层级 小白能感知到的作用
renderView 根渲染对象,承载所有 UI 渲染树(RenderObject 树) 屏幕尺寸、根布局
pipelineOwner 驱动布局、绘制、合成 渲染管线 UI 刷新、动画流畅度
buildOwner 驱动 Widget 树构建、更新 Widget 树 setState 刷新 UI

用户操作 → setState ↓ buildOwner(构建 Widget 结构) ↓ pipelineOwner(渲染到屏幕)

4. 手势 / 输入

  • gestureArenaGestureArenaManager:手势竞技场,处理手势(点击、滑动、长按)冲突,决定谁赢。
Dart 复制代码
import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('手势互斥:二选一')),
        body: const Center(child: GestureArenaDemo()),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      // 点击
      onTap: () {
        print("✅ 【点击获胜】滑动已被系统取消");
        // 查看竞技场
        final arena = WidgetsBinding.instance.gestureArena;
        arena.debugDescribe();
      },

      // 滑动
      onPanStart: (_) {
        print("✅ 【滑动获胜】点击已被系统取消");
        // 查看竞技场
        final arena = WidgetsBinding.instance.gestureArena;
        arena.debugDescribe();
      },

      child: Container(
        width: 300,
        height: 300,
        color: Colors.blueAccent,
        alignment: Alignment.center,
        child: const Text(
          '点我:只打印点击\n滑我:只打印滑动\n\n永远二选一!',
          style: TextStyle(color: Colors.white, fontSize: 24),
          textAlign: TextAlign.center,
        ),
      ),
    );
  }
}
  • mouseTrackerMouseTracker:鼠标指针追踪。
Dart 复制代码
import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: MouseRegion(
            child: Container(
              width: 300,
              height: 300,
              color: Colors.blue,
              alignment: Alignment.center,
              child: const Text("鼠标放我上面", style: TextStyle(color: Colors.white, fontSize: 24)),
            ),
            
            // 鼠标进入
            onEnter: (event) {
              print("✅ 鼠标进入");
              
              // 你要的 mouseTracker
              final mouseTracker = WidgetsBinding.instance.mouseTracker;
              print("鼠标追踪器状态:${mouseTracker.debugDescription}");
            },
            
            // 鼠标移动
            onHover: (event) {
              // 实时鼠标位置
              print("鼠标位置:${event.position}");
            },
          ),
        ),
      ),
    );
  }
}
Dart 复制代码
// 获取当前有哪些组件正在监听鼠标
mouseTracker.debugListTypedConnections<MouseTrackerAnnotation>();  
// 强制刷新鼠标状态  
mouseTracker.updateDeviceCursor(0);  
// 调试鼠标事件丢失、不触发 
print(mouseTracker.debugDescription);
  • keyboardHardwareKeyboard:硬件键盘状态查询。
Dart 复制代码
final keyboard = WidgetsBinding.instance.keyboard;
print('键盘是否按下 Shift: ${keyboard.isShiftPressed}');
  • focusManagerFocusManager:焦点树管理。
Dart 复制代码
final focus = WidgetsBinding.instance.focusManager;
print('当前焦点节点: ${focus.primaryFocus}');
// 拿走当前焦点 → 键盘自动消失
WidgetsBinding.instance.focusManager.primaryFocus?.unfocus();

5. 其他全局

  • platformDispatcherPlatformDispatcher:平台调度器(替代旧版 window)Flutter。
  • platformBrightness:返回 Brightness.light/Brightness.dark,用于适配深色模式
  • locales:获取系统当前的语言区域设置,用于多语言适配、监听屏幕尺寸变化、字体缩放、平台消息分发
Dart 复制代码
final dispatcher = WidgetsBinding.instance.platformDispatcher;
print('屏幕亮度: ${dispatcher.platformBrightness}'); // 获取系统亮/暗模式
print('语言: ${dispatcher.locales}'); // 获取系统语言列表
Dart 复制代码
import 'package:flutter/material.dart';
import 'dart:ui' as ui;

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

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

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

class _MyAppState extends State<MyApp> {
  // 屏幕信息
  double? width;
  double? height;
  double? textScale;

  // 平台分发消息(真正用 platformDispatcher 接收)
  String platformMessage = "等待平台消息...";

  @override
  void initState() {
    super.initState();

    // ================================
    // 🔥 1. 监听屏幕尺寸 + 字体缩放(官方正确用法)
    // ================================
    final dispatcher = WidgetsBinding.instance.platformDispatcher;

    // 先取初始值
    updateInfo();

    // 监听变化:屏幕旋转、尺寸变化、字体缩放、像素密度变化
    dispatcher.onMetricsChanged = () {
      setState(() {
        updateInfo();
      });
    };

    // ================================
    // 🔥 2. 真正用 platformDispatcher 接收平台消息
    // 这是引擎底层消息,不是 channel!
    // ================================
    dispatcher.onPlatformMessage = (
      String name,
      Uint8List? data,
      ui.PlatformMessageResponseCallback? callback,
    ) {
      setState(() {
        platformMessage = "收到平台消息:$name";
      });
      // 必须回复,否则引擎会报错
      callback?.call(null);
    };
  }

  // 更新屏幕 + 字体信息
  void updateInfo() {
    final view = WidgetsBinding.instance.platformDispatcher.views.first;
    final size = view.physicalSize / view.devicePixelRatio;
    width = size.width;
    height = size.height;
    textScale = WidgetsBinding.instance.platformDispatcher.textScaleFactor;
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text("platformDispatcher 纯官方演示")),
        body: Padding(
          padding: const EdgeInsets.all(20.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text("📱 屏幕宽度:${width?.toStringAsFixed(1)}", style: const TextStyle(fontSize: 16)),
              Text("📱 屏幕高度:${height?.toStringAsFixed(1)}", style: const TextStyle(fontSize: 16)),
              const SizedBox(height: 20),
              Text("🔤 系统字体缩放:${textScale?.toStringAsFixed(2)}", style: const TextStyle(fontSize: 16)),
              const SizedBox(height: 20),
              Text("📩 $platformMessage", style: const TextStyle(fontSize: 16, color: Colors.blue)),
            ],
          ),
        ),
      ),
    );
  }
}
  • firstFrameRasterizedbool:首帧是否已渲染完成。
Dart 复制代码
final firstFrameDone = WidgetsBinding.instance.firstFrameRasterized;
print('首帧是否渲染完成: $firstFrameDone');
  • isRootWidgetAttachedbool:根 Widget 是否已挂载。
Dart 复制代码
final attached = WidgetsBinding.instance.isRootWidgetAttached;
print('根 Widget 是否已挂载: $attached');

2、第三方库

2.1 easy_localization

pubspec.yaml

复制代码
dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  flutter_web_plugins:
    sdk: flutter
  easy_localization: ^3.0.0

assets/i18n下编写json文件

main.dart

  1. WidgetsFlutterBinding.ensureInitialized()
  2. await EasyLocalization.ensureInitialized()
  3. runApp(EasyLocalization(...))

EasyLocalization 参数你项目是这样设计的:

  • supportedLocales: 支持 7 种语言

  • path: 'assets/i18n'

  • fallbackLocale: Locale(LangConfig.defaultLang)

  • startLocale: Locale(LangConfig.defaultLang)

  • useFallbackTranslations: true

    import 'package:easy_localization/easy_localization.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    import 'package:flutter_native_splash/flutter_native_splash.dart';
    import 'package:get/get.dart' hide Trans;
    import 'package:get_storage/get_storage.dart';
    import 'config/lang_config.dart';
    import 'controllers/app_controller.dart';
    import 'controllers/user_controller.dart';
    import 'services/api_service.dart';
    import 'app.dart';
    // 默认导入 url_strategy_stub.dart
    // 如果当前编译目标支持 dart:html(也就是 Web),改为导入 url_strategy_web.dart
    // 最后统一起别名 url_strategy
    import 'url_strategy_stub.dart'
    if (dart.library.html) 'url_strategy_web.dart'
    as url_strategy;

    Future<void> main() async {
    // Flutter 框架没初始化则立刻初始化,若已经初始化则返回现有对象
    // binding: WidgetsBinding是flutter与引擎交互的总路口
    final binding = WidgetsFlutterBinding.ensureInitialized();
    // Web 使用哈希路由(localhost:63497/#/profile),与 Vue SPA 一致
    url_strategy.setupHashUrlStrategy();

    复制代码
    // 保留启动页
    FlutterNativeSplash.preserve(widgetsBinding: binding);
    
    await EasyLocalization.ensureInitialized();
    await GetStorage.init();
    // 初始化 API 服务
    ApiService().init();
    // 全局锁定竖屏,全屏视频页单独允许横屏
    SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
    Get.put(AppController());
    Get.put(UserController());
    
    // 同步当前语言到 UserController
    final currentLocale = WidgetsBinding.instance.platformDispatcher.locale;
    final langCode = currentLocale.languageCode;
    final countryCode = currentLocale.countryCode;
    String localeCode = langCode;
    if (langCode == 'zh' && countryCode == 'TW') {
      localeCode = 'zh_TW';
    }
    UserController.to.setLocale(localeCode);
    
    runApp(
      EasyLocalization(
        supportedLocales: const [
          Locale('en'),
          Locale('zh'),
          Locale('ko'),
          Locale('ja'),
          Locale('th'),
          Locale('zh', 'TW'),
          Locale('vi'),
        ],
        path: 'assets/i18n',
        fallbackLocale: Locale(LangConfig.defaultLang),
        startLocale: Locale(LangConfig.defaultLang),
        useFallbackTranslations: true,
        child: const SdramaApp(),
      ),
    );
    
    // 等到 Flutter 首帧完成后再移除启动页,避免首帧布局未完成时收到点击导致 hitTest 断言。
    WidgetsBinding.instance.addPostFrameCallback((_) {
      FlutterNativeSplash.remove();
    });

    }

app.dart

easy_localization 的上下文能力"桥接"到 MaterialApp

复制代码
import 'package:easy_localization/easy_localization.dart';  
class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    context.locale;
    // 初始化 ToastHelper
    ToastHelper.init(context);
    return Obx(
      () => MaterialApp(
        title: 'app.title'.tr(namedArgs: app_brand.AppConfig.brandNamedArgs),
        debugShowCheckedModeBanner: false,
        locale: context.locale,
        supportedLocales: context.supportedLocales,
        localizationsDelegates: context.localizationDelegates,
      ),
    );
  }
}

其它页面使用翻译和上面类似

切换语言

复制代码
import 'package:easy_localization/easy_localization.dart';

await context.setLocale(Locale('zh', 'TW');

3、内置包

3.1 services

复制代码
import 'package:flutter/services.dart';

作用:专门负责和原生系统交互的核心服务包。

  • 调用键盘、输入法
  • 监听、控制屏幕旋转
  • 调用原生通道(MethodChannel)
  • 复制、粘贴、剪切板
  • 控制状态栏、亮度、震动
  • 读取平台信息(iOS/Android)
  • 处理原生事件

3.1.1 键盘控制

隐藏键盘

复制代码
SystemChannels.textInput.invokeMethod('TextInput.hide');

或者

复制代码
import 'package:flutter/material.dart'; // 包含它
// 或
import 'package:flutter/widgets.dart';
FocusScope.of(context).unfocus();

打开键盘

复制代码
// 先获取焦点节点
FocusNode focusNode = FocusNode();

// 让输入框获得焦点 → 键盘自动弹出
FocusScope.of(context).requestFocus(focusNode);

完整案例(实际textField聚焦后会自动打开键盘)

复制代码
import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const AutoKeyboardPage(),
    );
  }
}

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

  @override
  State<AutoKeyboardPage> createState() => _AutoKeyboardPageState();
}

class _AutoKeyboardPageState extends State<AutoKeyboardPage> {
  // 1. 创建焦点节点
  late FocusNode _focusNode;

  @override
  void initState() {
    super.initState();
    _focusNode = FocusNode();

    // 2. 页面初始化后自动获取焦点 → 自动弹键盘
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _focusNode.requestFocus();
    });
  }

  @override
  void dispose() {
    // 记得释放
    _focusNode.dispose();
    super.dispose();
  }

  // 关闭键盘
  void _hideKeyboard() {
    FocusScope.of(context).unfocus();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("自动打开键盘")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              focusNode: _focusNode, // 绑定焦点
              decoration: const InputDecoration(
                labelText: "输入框",
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _hideKeyboard,
              child: const Text("关闭键盘"),
            ),
          ],
        ),
      ),
    );
  }
}

3.1.2 屏幕方向

复制代码
// 强制竖屏
SystemChrome.setPreferredOrientations([
  DeviceOrientation.portraitUp,
]);

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  // 强制竖屏:必须在 runApp 之前调用
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp, // 仅允许正竖屏
    // DeviceOrientation.landscapeLeft,  // 左横屏
    // DeviceOrientation.landscapeRight, // 右横屏
  ]);

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '强制竖屏 Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const HomePage(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("强制竖屏")),
      body: const Center(
        child: Text(
          "整个 App 已强制竖屏\n无论怎么旋转都不会横屏",
          textAlign: TextAlign.center,
          style: TextStyle(fontSize: 20),
        ),
      ),
    );
  }
}

android原生

android/app/src/main/AndroidManifest.xml

复制代码
android:screenOrientation="portrait"

android:screenOrientation="landscape"

ios原生

ios/Runner/Info.plist

复制代码
<key>UISupportedInterfaceOrientations</key>
<array>
  <string>UIInterfaceOrientationLandscapeLeft</string>
  <string>UIInterfaceOrientationLandscapeRight</string>
</array>

<key>UISupportedInterfaceOrientations</key>
<array>
  <string>UIInterfaceOrientationPortrait</string>
</array>

3.1.3 状态栏(顶部电池、时间栏)

复制代码
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  // 隐藏状态栏和导航栏,沉浸式模式
  SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '隐藏状态栏 Demo',
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: Text(
            '状态栏已隐藏\n(滑动屏幕边缘可呼出)',
            style: TextStyle(fontSize: 22),
            textAlign: TextAlign.center,
          ),
        ),
      ),
    );
  }
}

只隐藏状态栏,保留底部导航栏

复制代码
SystemChrome.setEnabledSystemUIMode(
  SystemUiMode.manual,
  overlays: [SystemUiOverlay.bottom], // 只显示底部导航栏
);

// 1. 只隐藏顶部状态栏(最常用)
overlays: [SystemUiOverlay.bottom]

// 2. 只隐藏底部导航栏
overlays: [SystemUiOverlay.top]

// 3. 全部显示(恢复正常)
overlays: SystemUiOverlay.values

底部导航栏 (已被"手势导航"替代)

复制代码
Android:屏幕底部有 系统导航栏(返回键、主页键、多任务键)
iOS:底部只有一条小横条,不算系统导航栏,也不能用 Flutter 隐藏

SystemUiMode 一共有 4 个常用枚举值

枚举值 意思(大白话) 效果
immersive 普通沉浸式 滑动呼出,不自动隐藏
immersiveSticky 粘性沉浸式 滑动呼出,几秒后自动隐藏(最常用)
edgeToEdge 边到边正常模式 显示状态栏 + 导航栏(默认)
manual 手动自定义 自己决定显示 / 隐藏哪个

3.1.4 剪切板

复制内容

复制代码
// 复制文字
await Clipboard.setData(ClipboardData(text: '复制内容'));

import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; // 必须导入

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text("复制文字示例")),
        body: const Center(
          child: CopyWidget(), // 复制功能组件
        ),
      ),
    );
  }
}

// 复制功能组件
class CopyWidget extends StatelessWidget {
  const CopyWidget({super.key});

  // 复制方法
  Future<void> copyText() async {
    // 复制内容
    await Clipboard.setData(ClipboardData(text: '我是被复制的内容'));
    
    // 复制成功后提示
    // 这里用 Flutter 自带提示,也可以用 Toast
    print("复制成功");
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: copyText,
      child: const Text("点击复制文字"),
    );
  }
}

3.1.5 原生通道

原生通道 MethodChannel(Flutter ↔ 原生通信)

复制代码
const channel = MethodChannel('my_channel');
  • 调用系统相机、相册

  • 获取设备唯一标识

  • 调用第三方 SDK(微信、支付宝、推送)

  • 读写本地文件、蓝牙、传感器

  • 启动原生页面

    import 'package:flutter/services.dart';

    // 1. 创建通道(名字必须唯一)
    const MethodChannel _channel = MethodChannel('my_channel');

    // 2. 调用原生方法
    Future<void> callNativeMethod() async {
    try {
    // 给原生发送消息,可接收返回值
    final String result = await _channel.invokeMethod(
    'getDeviceInfo', // 方法名
    {'extra': '123'}, // 传递参数
    );
    print('原生返回:result'); } on PlatformException catch (e) { print('错误:{e.message}');
    }
    }

安卓端写了一个方法

复制代码
override fun onMethodCall(call: MethodCall, result: Result) {
    if (call.method == "getDeviceInfo") { // 这里必须一模一样!
        // 安卓原生逻辑
    }
}

3.1.6 震动

Flutter 自带的 触觉反馈 / 震动 功能 点击反馈、提示震动

  • 安卓:短震动

  • iOS:轻触反馈(不吵人、很柔和)

    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart'; // 必须导入

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

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

    复制代码
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: const Text("震动示例")),
          body: Center(
            child: ElevatedButton(
              onPressed: () {
                // ✅ 触发震动
                HapticFeedback.vibrate();
              },
              child: const Text("点我震动"),
            ),
          ),
        ),
      );
    }

    }

    // 轻震动(最常用)
    HapticFeedback.lightImpact();

    // 中等震动
    HapticFeedback.mediumImpact();

    // 强震动
    HapticFeedback.heavyImpact();

    // 选择器震动(很轻)
    HapticFeedback.selectionClick();

相关推荐
jimmyleeee2 小时前
人工智能基础知识笔记三十九:几个Skills的网站
人工智能·笔记·chatgpt
Hello_Embed2 小时前
嵌入式上位机开发入门(二十二):RTU/TCP 双协议互斥访问寄存器
笔记·网络协议·tcp/ip·嵌入式
南宫萧幕2 小时前
自动控制原理|稳定性与劳斯判据 知识点+计算题+MATLAB实现全套笔记
笔记·matlab·控制
有个人神神叨叨2 小时前
Claude Managed Agents 快速入门笔记
人工智能·笔记
yuyuyuliang003 小时前
python笔记1
开发语言·笔记·python
lonelyhiker3 小时前
cas学习笔记
数据库·笔记·学习
不想学习\??!3 小时前
USB-HID学习笔记
笔记·学习
码农的小菜园4 小时前
提示工程学习笔记(一)
笔记·学习
四谎真好看4 小时前
Redis学习笔记(高级篇3)
redis·笔记·学习·学习笔记