Flutter多应用展示插件multi_app_viewer的使用

发布于 1周前 作者 yuanlaile 来自 Flutter

Flutter多应用展示插件multi_app_viewer的使用

1. 插件简介

Multi App Viewer 是一个开发工具,允许开发者同时查看和导航多个应用实例,每个实例可以有不同的视觉设置。这在开发过程中非常有用,可以帮助开发者快速检查不同设备尺寸、主题等的效果,展示响应式/自适应应用,创建截图和动画,以及进行设计对比和选择。

2. 主要功能

  • 布局方式:支持行、列、堆叠或复杂仪表板布局。
  • 同步导航:所有实例根据主实例进行同步导航。
  • 格式化、标题、注释、倾斜:可以对每个实例进行格式化、添加标题、注释和倾斜效果。
  • 状态广播:可以在多个实例之间广播状态变化。
  • 全屏模式:支持全屏切换。
  • 隐藏/显示应用栏:可以隐藏或显示应用栏。
  • 首选配置列表:可以选择预定义的配置。
  • 文本实例:可以添加HTML内容来突出显示某些主题,而无需显示应用屏幕。
  • 脚本支持:支持自动启动、按钮点击脚本、同步导航、事件广播和配置更改。
  • 自动重复:可以设置脚本自动重复执行。
  • 截图和录屏:可以保存截图(不包括MAV应用栏)并录制屏幕转换为GIF。

3. 使用场景

  • 快速检查表单因素和主题:在开发过程中快速检查不同设备尺寸和主题的效果。
  • 展示响应式/自适应应用:展示应用在不同设备上的响应式布局。
  • 自动生成截图和动画:方便地生成应用的截图和动画。
  • 设计对比和选择:比较不同设计方案,选择最佳方案。

4. 安装与配置

4.1 添加依赖

首先,在 pubspec.yaml 文件中添加 multi_app_viewer 依赖:

dependencies:
  multi_app_viewer: ^最新版本号

然后运行 flutter pub get 来安装依赖。

4.2 创建主文件

example/lib/main.dart 文件复制到你的项目中,并重命名为一个新的文件名(例如 mav_main.dart)。然后替换 YourApp() 为你自己的顶级小部件。

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

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MAV Demo',
      debugShowCheckedModeBanner: false,
      home: MavFrameWidget(
        itemBuilder: (MavItem mavItem) {
          /* Replace MyApp() with your app widget */
          return YourApp();
        },
      ),
    );
  }
}
4.3 创建新的运行配置

创建一个新的运行配置,目标指向你刚刚创建的文件(例如 mav_main.dart),然后运行或调试它。你应该会看到类似以下的界面,其中包含你的应用在不同设备屏幕上的展示:

First impression

5. 使用方法

5.1 交互式编辑器

通过工具栏切换到编辑模式,点击某个实例进行配置,或者点击空白处打开框架配置对话框。

5.2 初始视图

可以通过在 main 函数中调用 MavFrame.loadConfiguration 来设置初始视图。你可以选择预定义的配置、从代码创建配置,或者从文件加载配置。

void main() {
  MavFrame.loadConfiguration(
    // FrameConfiguration.fromFile('C:\\Users\\kl\\Documents\\mav_config.mavc')
    FrameConfiguration.base(),
  );
  runApp(const MavCounterDemo());
}
5.3 子实例自定义

在根小部件中添加 mavItem 参数,并使用 mavItem.configuration 字段来自定义每个应用实例。

class MyApp extends StatelessWidget {
  final MavItem? mavItem;
  MyApp({super.key, this.mavItem});
}

MaterialApp.router(
  builder: (context, rootWidget) {
    // 使用 mavItem 和主要的 mavItem.configuration 来自定义每个应用实例
    return Directionality(
      textDirection: item?.configuration.textDirection ?? TextDirection.ltr,
      child: Theme(
        data: Theme.of(context).copyWith(platform: mavItem?.configuration.targetPlatform),
        child: rootWidget!,
      ),
    );
  },
  // ...
)
5.4 同步导航

为了使所有实例根据主实例(第一个实例)进行同步导航,需要添加 NavigationObserver

late final GoRouter _router = GoRouter(
  // MAV 自定义
  navigatorKey: mavItem?.navigatorKey,
  observers: mavItem?.navigatorObserver == null ? null : [mavItem!.navigatorObserver],
  // ...
);

MaterialApp.router(builder: (context, rootWidget) {
  item?.onNavigationCallback = (String routeName, int index, {Object? arguments}) {
    if (index != mavItem?.index) {
      if (arguments != null) {
        _router.goNamed(routeName, queryParameters: arguments as Map<String, dynamic>);
      } else {
        _router.goNamed(routeName);
      }
    }
  };
  // ...
})
5.5 应用内部自定义

如果你想在应用内部使用 MavItem 配置,可以通过 MavItemInherited 获取它。例如,避免 Android 的 restorationId 冲突:

late final MavItem? mavItem = MavItemInherited.maybeOf(context)?.item;

[@override](/user/override)
String get restorationId => 'bottom_app_bar_demo${mavItem?.identifier ?? ''}';
5.6 进一步同步

Multi App Viewer 提供了一个简单的事件总线,可以在一个 MAV Item 中推送带标签的值,其他实例可以监听这些值。

int _counter = 0;
late final MavItem? mavItem = MavItemInherited.maybeOf(context)?.item;

void _incrementCounter() {
  setState(() {
    _counter++;
  });
  mavItem?.publish('_counterValue', _counter);
}

@Override
void dispose() {
  super.dispose();
  mavItem?.dispose('_counterValue');
}

@Override
Widget build(BuildContext context) {
  mavItem?.listen('_counterValue', (value) {
    setState(() {
      _counter = value;
    });
  });

  return Scaffold(
    //...
  );
}
5.7 脚本

脚本是导航、配置更改和同步命令的列表。例如:

List script = [
  FrameConfiguration.fourPiece(),
  // 导航到 details 页面,参数为 33
  (-1, 'details', {'id1': '33'}),
  // 返回主页面
  (-1, '/', {'id1': '33'}),
  // 发送值 99 给所有监听者
  ('_counterValue', 99)
];

// 当工具栏菜单中的“运行脚本”被调用时运行脚本
MavFrame.buttonScript = script;

// 当应用加载或重新加载时运行脚本
MavFrame.autoRunScript = script;

// 重复运行脚本,直到工具栏上的停止按钮被按下
MavFrame.autoRepeat = true;

6. 示例代码

以下是完整的示例代码,展示了如何使用 multi_app_viewer 插件来展示多个应用实例:

import './go_router_samples/main.dart';
import 'package:flutter/material.dart';
import 'package:multi_app_viewer/multi_app_viewer.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  MavFrame.loadConfiguration(
    // FrameConfiguration.fromFile('C:\\Users\\kissl\\Documents\\ggg.mavc')
    FrameConfiguration.lot(),
  );

  // 设置自动运行脚本
  MavFrame.autoRunScript = [
    for (var i in MavFrame.preferredList) ...[
      i,
      (-1, 'details', {'id1': '55'}),
      (-1, '/', {'id1': '33'}),
      // ('_counterValue', 99)
    ]
  ];

  // 设置工具栏菜单中的“运行脚本”按钮
  MavFrame.buttonScript = [
    MavFrame.preferredList[2],
    (-1, 'details', {'id1': '55'}),
    (-1, '/', {'id1': '33'}),
    ('_counterValue', 99)
  ];

  // 设置脚本自动重复
  MavFrame.autoRepeat = true;

  runApp(const MavGoRouterDemo());
}

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.grey),
      ),
      home: const MavPage(
        key: Key('NavPage'),
      ),
    );
  }
}

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

  [@override](/user/override)
  State<MavPage> createState() => _MavPageState();
}

class _MavPageState extends State<MavPage> {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MavFrameWidget(
      itemBuilder: (MavItem item) {
        // 替换 MyApp 为你的顶级小部件
        return MyApp(
          mavItem: item,
          key: Key(item.identifier),
        );
      },
    );
  }
}

更多关于Flutter多应用展示插件multi_app_viewer的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter多应用展示插件multi_app_viewer的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用multi_app_viewer插件来展示多个应用的示例代码。这个插件允许你在一个Flutter应用中嵌套并展示其他Flutter应用或网页。

首先,确保你已经在pubspec.yaml文件中添加了multi_app_viewer依赖:

dependencies:
  flutter:
    sdk: flutter
  multi_app_viewer: ^最新版本号 # 请替换为实际最新版本号

然后,运行flutter pub get来安装依赖。

接下来,你可以在你的Flutter项目中使用MultiAppViewer来展示多个应用。以下是一个简单的示例代码:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Multi App Viewer Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MultiAppViewerDemo(),
    );
  }
}

class MultiAppViewerDemo extends StatefulWidget {
  @override
  _MultiAppViewerDemoState createState() => _MultiAppViewerDemoState();
}

class _MultiAppViewerDemoState extends State<MultiAppViewerDemo> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Multi App Viewer Demo'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text('展示第一个应用(Flutter应用):', style: TextStyle(fontSize: 20)),
            SizedBox(height: 16),
            Expanded(
              child: MultiAppViewer(
                url: 'https://你的第一个Flutter应用的URL', // 替换为你的Flutter应用的URL
                // 如果是本地Flutter应用,可以使用local_app配置
                // localApp: LocalApp(
                //   bundlePath: 'path/to/your/local/flutter/app',
                // ),
                onLoaded: () {
                  print('第一个应用已加载');
                },
                onError: (error) {
                  print('加载第一个应用时出错: $error');
                },
              ),
            ),
            SizedBox(height: 32),
            Text('展示第二个应用(网页):', style: TextStyle(fontSize: 20)),
            SizedBox(height: 16),
            Expanded(
              child: MultiAppViewer(
                url: 'https://www.example.com', // 替换为你想展示的网页URL
                webOnly: true, // 因为是网页,所以设置为true
                onLoaded: () {
                  print('第二个应用(网页)已加载');
                },
                onError: (error) {
                  print('加载第二个应用(网页)时出错: $error');
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个Flutter应用,它使用MultiAppViewer来展示两个应用:一个是Flutter应用(通过URL加载),另一个是网页(也是通过URL加载)。

请注意:

  1. url参数应该是你的Flutter应用的URL(如果你将你的Flutter应用部署到了某个服务器上)或者本地Flutter应用的路径(如果使用localApp配置)。
  2. 对于本地Flutter应用,你需要确保localAppbundlePath指向正确的本地Flutter应用路径。
  3. webOnly参数用于指示加载的内容是否为网页。如果是网页,请设置为true

这个示例代码提供了一个基本的框架,你可以根据自己的需求进行扩展和修改。

回到顶部