Flutter自适应卡片展示插件flutter_adaptive_cards的使用

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

Flutter自适应卡片展示插件flutter_adaptive_cards的使用

Adaptive Cards for Flutter

我们决定构建一个Flutter实现的自适应卡片(Adaptive Cards),因为我们相信这两项技术的未来。通过Flutter,我们找到了一个令人兴奋的框架,用于超快速且跨平台的UI开发。而通过自适应卡片,我们可以结合行业标准,以结构化的方式交换卡片内容。在Neohelden,我们正在基于这两项技术构建我们的商业AI助手——您可以阅读我们的博客文章了解更多关于为什么构建这个库的信息。

安装

将以下内容添加到您的pubspec.yaml文件中:

dependencies:
  flutter_adaptive_cards: ^0.1.2

然后导入:

import 'package:flutter_adaptive_cards/flutter_adaptive_cards.dart';

使用

在Flutter中使用自适应卡片非常简单:所有你需要的是AdaptiveCard小部件。

警告:Markdown支持与ColumnSet内容对齐

由于Flutter Markdown包的问题 #171,引入了supportMarkdown标志到所有自适应卡片承包商中。此属性的默认值为true,为了与旧版本保持兼容性,旧版本不支持ColumnSets中的内容对齐。如果将其设置为false,则ColumnSets中的内容对齐将正常工作,但每个TextBlock都将没有Markdown渲染效果。一旦问题解决,此标志将会被移除。

加载一个自适应卡片

有几种构造函数可以从不同的来源加载AC。

  • AdaptiveCard.network:接受一个URL来下载有效负载并显示它。
  • AdaptiveCard.asset:接受一个资产路径从本地数据加载有效负载。
  • AdaptiveCard.memory:接受一个Map(可以通过解码字符串使用json类获得)并显示它。

主机配置

HostConfig可以通过每个构造函数的两个参数进行配置:

  1. 参数hostConfigPath接受一个静态的HostConfig,可以存储为本地资产。在这种情况下,HostConfig必须添加到项目的pubspec.yaml中。
  2. 参数hostConfig接受一个动态的HostConfig作为字符串。这可以轻松地编程,并可用于在亮色主题和暗色主题之间切换。

如果同时设置了这两个参数,则将使用hostConfig参数。

示例

AdaptiveCard.network(
  placeholder: Text("Loading, please wait"), // 加载时显示的占位符
  url: "www.someUrlThatPoints.To/A.json", // 自适应卡片的有效负载URL
  hostConfigPath: "assets/host_config.json", // 主机配置路径
  onSubmit: (map) { // 提交事件处理程序
    // 发送到服务器或本地处理
  },
  onOpenUrl: (url) { // 打开URL事件处理程序
    // 使用浏览器打开URL或其他方式处理
  },
  // 如果设置此选项,则每个自适应卡片旁边都会出现一个按钮,点击后会显示有效负载。
  // 注意:仅在调试模式下可见,对于发布构建无影响。
  // 这对于调试非常有用
  showDebugJson: true,
  // 如果您尚未实现显式暗色主题,自适应卡片将尝试近似其颜色以匹配暗色主题,
  // 从而保持对比度和颜色意义不变。
  // 关闭此选项,如果您想在使用暗色主题时完全控制颜色。
  // 注意:此功能目前仍在开发中
  approximateDarkThemeColors: true,
);

示例应用

我们尝试在这个仓库的示例应用中展示所有由自适应卡片组件支持的可能配置参数。如果我们遗漏了任何内容,请随时打开一个问题。

测试运行

只需输入以下命令即可运行测试:

flutter test

要更新黄金文件,请运行:

flutter test --update-goldens test/sample_golden_test.dart

这将更新示例卡片的黄金文件。

贡献

请阅读CONTRIBUTING.md了解我们的行为准则以及提交拉取请求的流程。

作者

  • Norbert Kozsir (@Norbert515) – 初始工作,Neohelden GmbH前Flutter开发负责人
  • Pascal Stech (@Curvel) – 维护者,Neohelden GmbH Flutter开发者(NeoSEALs团队)
  • Maik Hummel (@Beevelop) – 维护者,Neohelden GmbH CTO(NeoSEALs团队)

查看参与此项目的贡献者列表

许可证

该项目根据MIT许可证授权——详见LICENSE文件。


示例代码

以下是完整的示例代码:

import 'package:dynamic_theme/dynamic_theme.dart';
import 'package:example/action_set/action_set_page.dart';
import 'package:example/custom_host_config/custom_host_config.dart';
import 'package:example/render_time/render_time_page.dart';
import 'package:example/samples/samples.dart';
import 'package:example/text_block/text_block_examples_page.dart';
import 'package:flutter/foundation.dart' show debugDefaultTargetPlatformOverride;
import 'package:flutter/material.dart';

import 'about_page.dart';
import 'action_open_url/action_open_url_examples_page.dart';
import 'action_show_card/action_show_card_examples_page.dart';
import 'action_submit/action_submit_examples_page.dart';
import 'brightness_switch.dart';
import 'column/column_examples_page.dart';
import 'column_set/column_set_examples_page.dart';
import 'container/container_examples_page.dart';
import 'fact_set/fact_set_examples_page.dart';
import 'image/image_examples_page.dart';
import 'image_set/image_set_examples_page.dart';
import 'inputs/input_choice_set/input_choice_set.dart';
import 'inputs/input_date/input_date.dart';
import 'inputs/input_number/input_number.dart';
import 'inputs/input_text/input_text.dart';
import 'inputs/input_time/input_time.dart';
import 'inputs/input_toggle/input_toggle.dart';
import 'media/media.dart';

void main() {
  debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return DynamicTheme(
      defaultBrightness: Brightness.dark,
      data: (brightness) => new ThemeData(
        primarySwatch: Colors.blue,
        brightness: brightness,
      ),
      themedWidgetBuilder: (context, theme) {
        return new MaterialApp(
          title: 'Flutter Adaptive Cards',
          theme: theme,
          home: new MyHomePage(),
          routes: {
            "Samples": (context) => SamplesPage(),
            "Samples with dynamic HostConfig": (context) => DynamicHostConfigPage(),
            "TextBlock": (context) => TextBlockPage(),
            "Image": (context) => ImagePage(),
            "Container": (context) => ContainerPage(),
            "ColumnSet": (context) => ColumnSetPage(),
            "Column": (context) => ColumnPage(),
            "FactSet": (context) => FactSetPage(),
            "ImageSet": (context) => ImageSetPage(),
            "ActionSet": (context) => ActionSetPage(),
            "Action.OpenUrl": (context) => ActionOpenUrlPage(),
            "Action.Submit": (context) => ActionSubmitPage(),
            "Action.ShowCard": (context) => ActionShowCardPage(),
            "Input.Text": (context) => InputText(),
            "Input.Number": (context) => InputNumber(),
            "Media": (context) => MediaPage(),
            "Input.Date": (context) => InputDatePage(),
            "Input.Time": (context) => InputTimePage(),
            "Input.Toggle": (context) => InputTogglePage(),
            "Input.ChoiceSet": (context) => InputChoiceSetPage(),
            "about": (context) => AboutPage(),
            "Render Time": (context) => RenderTimePage()
          },
        );
      },
    );
  }
}

class MyHomePage extends StatefulWidget {
  [@override](/user/override)
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Flutter Adaptive Cards"),
        actions: [
          BrightnessSwitch(),
          MaterialButton(
            onPressed: () {
              Navigator.of(context).pushNamed("about");
            },
            child: Text(
              "About",
              style: TextStyle(color: Colors.white),
            ),
          ),
        ],
      ),
      body: ListView(
        padding: EdgeInsets.all(16.0),
        children: <Widget>[
          Card(
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: Column(
                children: <Widget>[
                  Image.asset(
                    "assets/banner.jpg",
                  ),
                  Divider(),
                  Text(
                    "Flutter-Adaptive Cards by Neohelden",
                    style: TextStyle(fontSize: 24.0, fontWeight: FontWeight.w600),
                    textAlign: TextAlign.center,
                  ),
                ],
              ),
            ),
          ),
          getButton("Samples"),
          getButton("Samples with dynamic HostConfig"),
          getRow(["Image", "ImageSet"]),
          getButton("Media"),
          Divider(),
          getRow(["Action.OpenUrl", "Action.Submit", "Action.ShowCard"]),
          getButton("ActionSet"),
          Divider(),
          getButton("Container"),
          getButton("FactSet"),
          getButton("TextBlock"),
          getRow(["Column", "ColumnSet"]),
          Divider(),
          getRow(["Input.Text", "Input.Number", "Input.Date"]),
          getRow(["Input.Time", "Input.Toggle", "Input.ChoiceSet"]),
          Divider(),
          getButton("Render Time"),
        ],
      ),
    );
  }

  Widget getRow(List<String> element) {
    return Row(
      children: element
          .map(
            (it) => Expanded(child: getButton(it)),
          )
          .toList(),
    );
  }

  Widget getButton(String element) {
    return Card(
      child: InkWell(
          onTap: () => pushNamed(element),
          child: SizedBox(
            height: 64.0,
            child: Center(child: Text(element)),
          )),
    );
  }

  void pushNamed(String element) {
    Navigator.pushNamed(context, element);
  }
}

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

1 回复

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


flutter_adaptive_cards 是一个用于在 Flutter 应用中展示自适应卡片的插件。自适应卡片是一种基于 JSON 的卡片格式,最初由 Microsoft 的 Adaptive Cards 项目提出,用于在不同平台上展示一致的用户界面。

安装插件

首先,你需要在 pubspec.yaml 文件中添加 flutter_adaptive_cards 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_adaptive_cards: ^0.1.0  # 请检查最新版本

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

基本用法

  1. 导入包

    在你的 Dart 文件中导入 flutter_adaptive_cards 包:

    import 'package:flutter_adaptive_cards/flutter_adaptive_cards.dart';
  2. 创建自适应卡片

    你可以通过 JSON 字符串来定义自适应卡片。以下是一个简单的例子:

    final String cardJson = '''
    {
      "type": "AdaptiveCard",
      "version": "1.0",
      "body": [
        {
          "type": "TextBlock",
          "text": "Hello, World!",
          "size": "large"
        },
        {
          "type": "Image",
          "url": "https://via.placeholder.com/150"
        }
      ]
    }
    ''';
  3. 展示卡片

    使用 AdaptiveCard 组件来展示卡片:

    class MyHomePage extends StatelessWidget {
      [@override](/user/override)
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Adaptive Card Example'),
          ),
          body: Center(
            child: AdaptiveCard(
              cardJson: cardJson,
            ),
          ),
        );
      }
    }

处理用户交互

自适应卡片可以包含交互元素,如按钮、输入框等。你可以通过 onAction 回调来处理用户的交互:

AdaptiveCard(
  cardJson: cardJson,
  onAction: (action) {
    print('Action performed: ${action.type}');
    if (action.type == 'Action.Submit') {
      // 处理提交操作
      print('Submit data: ${action.data}');
    }
  },
);

自定义样式

你可以通过 AdaptiveTheme 来自定义卡片的样式:

AdaptiveTheme(
  data: AdaptiveThemeData(
    textBlock: TextStyle(
      fontSize: 16,
      color: Colors.blue,
    ),
    // 其他样式配置
  ),
  child: AdaptiveCard(
    cardJson: cardJson,
  ),
);

完整示例

以下是一个完整的示例,展示了如何使用 flutter_adaptive_cards 插件:

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

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Adaptive Card Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final String cardJson = '''
  {
    "type": "AdaptiveCard",
    "version": "1.0",
    "body": [
      {
        "type": "TextBlock",
        "text": "Hello, World!",
        "size": "large"
      },
      {
        "type": "Image",
        "url": "https://via.placeholder.com/150"
      },
      {
        "type": "Input.Text",
        "id": "inputText",
        "placeholder": "Enter some text"
      },
      {
        "type": "ActionSet",
        "actions": [
          {
            "type": "Action.Submit",
            "title": "Submit"
          }
        ]
      }
    ]
  }
  ''';

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Adaptive Card Example'),
      ),
      body: Center(
        child: AdaptiveCard(
          cardJson: cardJson,
          onAction: (action) {
            print('Action performed: ${action.type}');
            if (action.type == 'Action.Submit') {
              print('Submit data: ${action.data}');
            }
          },
        ),
      ),
    );
  }
}
回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!