Files
oneapp_docs/service_component/oneapp_popup.md
2025-09-24 14:08:54 +08:00

16 KiB

OneApp Popup - 弹窗管理模块

模块概述

oneapp_popup 是 OneApp 的统一弹窗管理模块,提供了全局弹窗管理、优先级控制、样式定制和生命周期管理等功能。该模块确保应用中所有弹窗的一致性体验,避免弹窗冲突,并提供灵活的弹窗展示策略。

核心功能

1. 统一弹窗管理

  • 全局队列:统一管理所有弹窗显示队列
  • 弹窗注册:注册和管理不同类型的弹窗
  • 显示控制:控制弹窗的显示和隐藏
  • 状态管理:跟踪弹窗的显示状态

2. 弹窗优先级控制

  • 优先级排序:按优先级排序弹窗显示顺序
  • 抢占机制:高优先级弹窗抢占显示
  • 队列管理:智能管理弹窗等待队列
  • 冲突解决:解决弹窗显示冲突

3. 弹窗样式定制

  • 主题适配:自动适配应用主题
  • 样式继承:支持样式继承和覆盖
  • 动画效果:丰富的弹窗动画效果
  • 响应式设计:适配不同屏幕尺寸

4. 弹窗生命周期

  • 生命周期钩子:完整的生命周期回调
  • 自动销毁:弹窗自动销毁机制
  • 内存管理:防止内存泄漏
  • 状态恢复:应用重启后状态恢复

技术架构

架构设计

┌─────────────────────────────────────┐
│            应用界面层                │
│         (Application UI)            │
├─────────────────────────────────────┤
│         OneApp Popup                │
│  ┌──────────┬──────────┬──────────┐ │
│  │ 弹窗管理 │ 队列控制 │ 样式系统 │ │
│  ├──────────┼──────────┼──────────┤ │
│  │ 生命周期 │ 事件系统 │ 动画引擎 │ │
│  └──────────┴──────────┴──────────┘ │
├─────────────────────────────────────┤
│           UI 渲染层                 │
│  ┌──────────┬──────────┬──────────┐ │
│  │ Overlay  │ Dialog   │ BottomSheet │
│  └──────────┴──────────┴──────────┘ │
├─────────────────────────────────────┤
│          Flutter Framework          │
│         (Widget System)             │
└─────────────────────────────────────┘

核心组件

1. 弹窗管理器 (PopupManager)

class PopupManager {
  // 显示弹窗
  Future<T?> show<T>(Popup popup);
  
  // 隐藏弹窗
  Future<bool> hide(String popupId);
  
  // 隐藏所有弹窗
  Future<void> hideAll();
  
  // 获取当前弹窗
  Popup? getCurrentPopup();
  
  // 获取弹窗队列
  List<Popup> getPopupQueue();
}

2. 弹窗队列控制器 (PopupQueueController)

class PopupQueueController {
  // 添加弹窗到队列
  void enqueue(Popup popup);
  
  // 从队列移除弹窗
  bool dequeue(String popupId);
  
  // 按优先级排序队列
  void sortByPriority();
  
  // 处理下一个弹窗
  Future<void> processNext();
}

3. 弹窗样式管理器 (PopupStyleManager)

class PopupStyleManager {
  // 应用弹窗主题
  PopupTheme applyTheme(PopupStyle style, AppTheme theme);
  
  // 创建弹窗样式
  PopupStyle createStyle(PopupType type);
  
  // 自定义样式
  PopupStyle customizeStyle(PopupStyle base, StyleOverrides overrides);
  
  // 获取默认样式
  PopupStyle getDefaultStyle(PopupType type);
}

4. 弹窗生命周期管理器 (PopupLifecycleManager)

class PopupLifecycleManager {
  // 注册生命周期监听器
  void registerLifecycleListener(String popupId, PopupLifecycleListener listener);
  
  // 触发生命周期事件
  void triggerLifecycleEvent(String popupId, PopupLifecycleEvent event);
  
  // 清理弹窗资源
  void cleanupPopup(String popupId);
  
  // 恢复弹窗状态
  Future<void> restorePopupState();
}

数据模型

弹窗模型

class Popup {
  final String id;
  final PopupType type;
  final Widget content;
  final PopupPriority priority;
  final PopupStyle? style;
  final PopupOptions options;
  final Map<String, dynamic> data;
  final DateTime createdAt;
  final Duration? autoHideDelay;
  final List<PopupAction> actions;
}

enum PopupType {
  dialog,        // 对话框
  bottomSheet,   // 底部弹窗
  toast,         // 吐司消息
  overlay,       // 覆盖层
  modal,         // 模态框
  banner,        // 横幅
  snackbar      // 快捷消息
}

enum PopupPriority {
  low,          // 低优先级
  normal,       // 普通优先级
  high,         // 高优先级
  critical,     // 关键优先级
  emergency     // 紧急优先级
}

弹窗样式模型

class PopupStyle {
  final Color? backgroundColor;
  final BorderRadius? borderRadius;
  final EdgeInsets? padding;
  final EdgeInsets? margin;
  final BoxShadow? shadow;
  final TextStyle? textStyle;
  final PopupAnimation? animation;
  final Alignment? alignment;
  final Size? size;
  final bool? barrierDismissible;
}

class PopupAnimation {
  final AnimationType type;
  final Duration duration;
  final Curve curve;
  final Offset? slideDirection;
  final double? scaleStart;
}

enum AnimationType {
  fade,         // 淡入淡出
  slide,        // 滑动
  scale,        // 缩放
  rotate,       // 旋转
  bounce,       // 弹跳
  none          // 无动画
}

弹窗选项模型

class PopupOptions {
  final bool barrierDismissible;
  final Color? barrierColor;
  final String? barrierLabel;
  final bool useRootNavigator;
  final RouteSettings? routeSettings;
  final Offset? anchorPoint;
  final PopupGravity gravity;
  final Duration? showDuration;
  final Duration? hideDuration;
}

enum PopupGravity {
  center,       // 居中
  top,          // 顶部
  bottom,       // 底部
  left,         // 左侧
  right,        // 右侧
  topLeft,      // 左上
  topRight,     // 右上
  bottomLeft,   // 左下
  bottomRight   // 右下
}

API 接口

弹窗管理接口

abstract class PopupService {
  // 显示弹窗
  Future<ApiResponse<T?>> showPopup<T>(ShowPopupRequest request);
  
  // 隐藏弹窗
  Future<ApiResponse<bool>> hidePopup(HidePopupRequest request);
  
  // 获取弹窗状态
  Future<ApiResponse<PopupStatus>> getPopupStatus(String popupId);
  
  // 清理弹窗
  Future<ApiResponse<bool>> clearPopups(ClearPopupsRequest request);
}

弹窗样式接口

abstract class PopupStyleService {
  // 获取弹窗主题
  Future<ApiResponse<PopupTheme>> getPopupTheme(String themeId);
  
  // 更新弹窗样式
  Future<ApiResponse<bool>> updatePopupStyle(UpdateStyleRequest request);
  
  // 获取样式模板
  Future<ApiResponse<List<PopupStyleTemplate>>> getStyleTemplates();
}

配置管理

弹窗配置

class PopupConfig {
  final int maxConcurrentPopups;
  final Duration defaultAnimationDuration;
  final bool enableGlobalQueue;
  final bool autoHideOnAppBackground;
  final Map<PopupType, PopupTypeConfig> typeConfigs;
  final PopupTheme defaultTheme;
  
  static const PopupConfig defaultConfig = PopupConfig(
    maxConcurrentPopups: 3,
    defaultAnimationDuration: Duration(milliseconds: 300),
    enableGlobalQueue: true,
    autoHideOnAppBackground: true,
    typeConfigs: {
      PopupType.dialog: PopupTypeConfig(
        maxInstances: 1,
        defaultPriority: PopupPriority.high,
      ),
      PopupType.toast: PopupTypeConfig(
        maxInstances: 5,
        defaultPriority: PopupPriority.low,
      ),
    },
    defaultTheme: PopupTheme.defaultTheme,
  );
}

主题配置

class PopupTheme {
  final PopupStyle dialogStyle;
  final PopupStyle toastStyle;
  final PopupStyle bottomSheetStyle;
  final PopupStyle overlayStyle;
  final Map<String, PopupStyle> customStyles;
  
  static const PopupTheme defaultTheme = PopupTheme(
    dialogStyle: PopupStyle(
      backgroundColor: Colors.white,
      borderRadius: BorderRadius.circular(8.0),
      padding: EdgeInsets.all(16.0),
      animation: PopupAnimation(
        type: AnimationType.scale,
        duration: Duration(milliseconds: 300),
      ),
    ),
    toastStyle: PopupStyle(
      backgroundColor: Colors.black87,
      borderRadius: BorderRadius.circular(24.0),
      padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
      animation: PopupAnimation(
        type: AnimationType.slide,
        duration: Duration(milliseconds: 200),
      ),
    ),
  );
}

使用示例

基本弹窗显示

// 显示对话框
final result = await PopupManager.instance.show<bool>(
  Popup(
    id: 'confirm_dialog',
    type: PopupType.dialog,
    priority: PopupPriority.high,
    content: AlertDialog(
      title: Text('确认操作'),
      content: Text('是否确认执行此操作?'),
      actions: [
        TextButton(
          onPressed: () => Navigator.of(context).pop(false),
          child: Text('取消'),
        ),
        TextButton(
          onPressed: () => Navigator.of(context).pop(true),
          child: Text('确认'),
        ),
      ],
    ),
  ),
);

if (result == true) {
  print('用户确认操作');
}

吐司消息

// 显示成功消息
PopupManager.instance.show(
  Popup(
    id: 'success_toast',
    type: PopupType.toast,
    priority: PopupPriority.normal,
    content: Container(
      padding: EdgeInsets.all(16.0),
      decoration: BoxDecoration(
        color: Colors.green,
        borderRadius: BorderRadius.circular(8.0),
      ),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(Icons.check, color: Colors.white),
          SizedBox(width: 8.0),
          Text('操作成功', style: TextStyle(color: Colors.white)),
        ],
      ),
    ),
    options: PopupOptions(
      gravity: PopupGravity.top,
      barrierDismissible: false,
    ),
    autoHideDelay: Duration(seconds: 3),
  ),
);

底部弹窗

// 显示底部选择弹窗
final selectedOption = await PopupManager.instance.show<String>(
  Popup(
    id: 'bottom_sheet_options',
    type: PopupType.bottomSheet,
    priority: PopupPriority.normal,
    content: Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        ListTile(
          leading: Icon(Icons.camera),
          title: Text('拍照'),
          onTap: () => Navigator.of(context).pop('camera'),
        ),
        ListTile(
          leading: Icon(Icons.photo_library),
          title: Text('从相册选择'),
          onTap: () => Navigator.of(context).pop('gallery'),
        ),
        ListTile(
          leading: Icon(Icons.cancel),
          title: Text('取消'),
          onTap: () => Navigator.of(context).pop(null),
        ),
      ],
    ),
    style: PopupStyle(
      backgroundColor: Colors.white,
      borderRadius: BorderRadius.vertical(top: Radius.circular(16.0)),
    ),
  ),
);

print('用户选择: $selectedOption');

自定义弹窗样式

// 创建自定义样式弹窗
final customStyle = PopupStyle(
  backgroundColor: Colors.blue.shade50,
  borderRadius: BorderRadius.circular(12.0),
  padding: EdgeInsets.all(20.0),
  shadow: BoxShadow(
    color: Colors.black26,
    blurRadius: 10.0,
    offset: Offset(0, 4),
  ),
  animation: PopupAnimation(
    type: AnimationType.bounce,
    duration: Duration(milliseconds: 500),
    curve: Curves.elasticOut,
  ),
);

PopupManager.instance.show(
  Popup(
    id: 'custom_popup',
    type: PopupType.modal,
    priority: PopupPriority.high,
    style: customStyle,
    content: Container(
      width: 300,
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(Icons.info, size: 48, color: Colors.blue),
          SizedBox(height: 16),
          Text('自定义弹窗', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
          SizedBox(height: 8),
          Text('这是一个自定义样式的弹窗示例'),
          SizedBox(height: 16),
          ElevatedButton(
            onPressed: () => PopupManager.instance.hide('custom_popup'),
            child: Text('关闭'),
          ),
        ],
      ),
    ),
  ),
);

弹窗队列管理

// 批量添加弹窗到队列
final popups = [
  Popup(
    id: 'popup_1',
    type: PopupType.toast,
    priority: PopupPriority.low,
    content: Text('消息1'),
  ),
  Popup(
    id: 'popup_2',
    type: PopupType.dialog,
    priority: PopupPriority.high,
    content: Text('重要消息'),
  ),
  Popup(
    id: 'popup_3',
    type: PopupType.toast,
    priority: PopupPriority.normal,
    content: Text('消息3'),
  ),
];

// 按优先级添加到队列
for (final popup in popups) {
  PopupManager.instance.enqueue(popup);
}

// 监听弹窗队列变化
PopupManager.instance.onQueueChanged.listen((queue) {
  print('当前队列长度: ${queue.length}');
});

测试策略

单元测试

group('PopupManager Tests', () {
  test('should show popup successfully', () async {
    // Given
    final popupManager = PopupManager();
    final popup = Popup(
      id: 'test_popup',
      type: PopupType.dialog,
      content: Text('Test'),
    );
    
    // When
    final result = await popupManager.show(popup);
    
    // Then
    expect(popupManager.getCurrentPopup()?.id, 'test_popup');
  });
  
  test('should manage popup queue by priority', () {
    // Given
    final queueController = PopupQueueController();
    final lowPriorityPopup = Popup(id: 'low', priority: PopupPriority.low);
    final highPriorityPopup = Popup(id: 'high', priority: PopupPriority.high);
    
    // When
    queueController.enqueue(lowPriorityPopup);
    queueController.enqueue(highPriorityPopup);
    queueController.sortByPriority();
    
    // Then
    final queue = queueController.getQueue();
    expect(queue.first.id, 'high');
    expect(queue.last.id, 'low');
  });
});

集成测试

group('Popup Integration Tests', () {
  testWidgets('popup display flow', (tester) async {
    // 1. 显示弹窗
    await PopupManager.instance.show(testPopup);
    await tester.pumpAndSettle();
    
    // 2. 验证弹窗显示
    expect(find.byKey(Key('test_popup')), findsOneWidget);
    
    // 3. 点击关闭按钮
    await tester.tap(find.byKey(Key('close_button')));
    await tester.pumpAndSettle();
    
    // 4. 验证弹窗关闭
    expect(find.byKey(Key('test_popup')), findsNothing);
  });
});

性能优化

渲染优化

  • 懒加载:弹窗内容懒加载
  • 复用机制:弹窗组件复用
  • 动画优化:高效的动画实现
  • 内存管理:及时释放弹窗资源

队列优化

  • 智能调度:智能弹窗调度算法
  • 优先级合并:相同优先级弹窗合并
  • 批量处理:批量处理弹窗队列
  • 负载控制:控制同时显示的弹窗数量

版本历史

v0.3.1+2 (当前版本)

  • 新增弹窗主题系统
  • 优化动画性能
  • 支持自定义弹窗样式
  • 修复内存泄漏问题

v0.3.0

  • 重构弹窗管理架构
  • 新增优先级队列系统
  • 支持多种弹窗类型
  • 改进生命周期管理

依赖关系

内部依赖

  • basic_theme: 主题管理系统
  • basic_utils: 工具类库
  • basic_logger: 日志记录

外部依赖

  • flutter/material: Material 设计组件
  • flutter/cupertino: iOS 风格组件
  • rxdart: 响应式编程支持

总结

oneapp_popup 模块为 OneApp 提供了完整的弹窗管理解决方案。通过统一的弹窗管理、智能的优先级控制、灵活的样式定制和完善的生命周期管理,该模块确保了应用中所有弹窗的一致性体验,提高了用户界面的专业性和用户体验的流畅性。