Flutter UI组件库插件bones_ui的使用

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

Flutter UI组件库插件bones_ui的使用

Bones_UI 是一个为 Dart 提供的直观且用户友好的 Web 用户界面框架。本文将介绍如何在你的 Flutter 项目中使用 Bones_UI 组件库。

CLI 工具

你可以使用 bones_ui 的命令行工具来创建或运行项目:

激活 CLI 工具

$> dart pub global activate bones_ui

查看帮助信息

$> bones_ui --help

运行单元测试

$> bones_ui test

更多详细信息可以通过以下命令查看:

$> bones_ui test --help

创建项目

$> bones_ui create -o /path/to/workspace -p project_name_dir=simple_project -p "project_name=Simple Project"

使用示例

以下是一个简单的使用示例,展示如何创建和配置一个 Bones_UI 应用:

import 'dart:html';
import 'package:bones_ui/bones_ui_kit.dart';

void main() async {
  // 创建 `bones_ui` 根并初始化它:
  var root = MyUIRoot(querySelector('#output'));
  root.initialize();
}

// `Bones_UI` UI 根。
class MyUIRoot extends UIRoot {
  MyUIRoot(Element? rootContainer) : super(rootContainer);

  MyMenu _menu;
  MyHome _home;

  @override
  void configure() {
    _menu = MyMenu(content);
    _home = MyHome(content);
  }

  // 返回菜单组件。
  @override
  UIComponent renderMenu() => _menu;

  // 返回内容组件。
  @override
  UIComponent renderContent() => _home;
}

// 顶部菜单。
class MyMenu extends UIComponent {
  MyMenu(Element parent) : super(parent);

  // 渲染一个固定的顶部菜单,带有标题。
  @override
  dynamic render() {
    return $div(
        classes: 'menu',
        style: 'position: fixed; top: 0; left: 0; width: 100%; background-color: black; color: white; padding: 10px',
        content: '<span style="font-size: 120%; font-weight: bold">Bones_UI: '
            '<a href="#register">Register</a> | '
            '<a href="#login">Login</a>'
            '</span>'
    );
  }
}

// 主页组件。
class MyHome extends UIComponent {
  MyHome(Element parent) : super(parent);

  @override
  dynamic render() {
    return markdownToDiv(('''
    <br>
    
    # Home
    
    Welcome!
    
    This is a VERY simple example!
    '''));
  }
}

示例代码

以下是一个更完整的示例,展示了如何使用 Bones_UI 来构建一个包含导航、页脚和可导航内容的应用:

import 'dart:html';
import 'package:bones_ui/bones_ui_kit.dart';

void main() async {
  // 创建 `bones_ui` 根并初始化它:
  var root = MyRoot(querySelector('#output'));
  root.initialize();
}

// `Bones_UI` 根。
class MyRoot extends UIRoot {
  MyRoot(super.rootContainer);

  MyMenu? _menu;
  MyFooter? _footer;
  MyNavigable? _navigable;

  @override
  void configure() {
    _menu = MyMenu(content);
    _footer = MyFooter(content);
    _navigable = MyNavigable(content);
  }

  // 返回菜单组件。
  @override
  UIComponent? renderMenu() => _menu;

  // 返回页脚组件。
  @override
  UIComponent? renderFooter() => _footer;

  // 返回内容组件。
  @override
  UIComponent? renderContent() => _navigable;
}

// 顶部菜单。
class MyMenu extends UIComponent {
  MyMenu(super.parent);

  // 渲染一个固定的顶部菜单,带有“首页”和“帮助”部分。
  @override
  dynamic render() {
    return $div(
        style:
            'position: fixed; top: 0; left: 0; width: 100%; background-color: black; color: white; padding: 10px',
        content: [
          $span(
              content:
                  '<span style="font-size: 120%; font-weight: bold" navigate="home">Bones_UI &nbsp; - &nbsp;</span>'),
          $span(attributes: {'navigate': 'home'}, content: 'Home'),
          '<span> &nbsp; | &nbsp; </span>',
          $span(attributes: {'navigate': 'components'}, content: 'Components'),
          '<span> &nbsp; | &nbsp; </span>',
          $span(attributes: {'navigate': 'help'}, content: 'Help')
        ]);
  }
}

// 页脚
class MyFooter extends UIComponent {
  MyFooter(super.parent);

  // 渲染一个固定的底部菜单。
  @override
  dynamic render() {
    return $div(
        style:
            'position: absolute; position: fixed; bottom: 0; left: 0; width: 100%; background-color: rgba(0,0,0, 0.05); color: black; padding: 4px',
        content: [
          $span(
              content:
                  '<span style="font-size: 90%;" navigate="home">Built with <a href="https://colossus-services.github.io/bones_ui/" target="_blank">Bones_UI</a></span>'),
        ]);
  }
}

// 可导航的内容,根据当前路由变化。
class MyNavigable extends UINavigableComponent {
  MyNavigable(Element? parent) : super(parent, ['home', 'components', 'help']);

  @override
  dynamic renderRoute(String? route, Map<String, String>? parameters) {
    print('renderRoute> $route');
    switch (route) {
      case 'home':
        return MyHome(content);
      case 'components':
        return MyComponents(content);
      case 'help':
        return MyHelp(content);
      default:
        return '?';
    }
  }
}

// “主页”路由。
class MyHome extends UIComponent {
  MyHome(super.parent);

  @override
  dynamic render() {
    return markdownToDiv(('''
    <br>
    
    # Home
    
    Welcome to `Bones_UI` example
    
    This is a VERY simple example!
    
    See the [Help section](#help) for more
    
    '''));
  }
}

// “帮助”路由。
class MyHelp extends UIComponent {
  MyHelp(super.parent);

  @override
  dynamic render() {
    return $divInline(
        style: 'width: 300px ; max-width:80vw; text-align: left',
        content: [
          markdownToDiv('''
          <br>
    
          # Help
    
          See our FAQ for help:
    
          ## FAQ
    
          - Is `Bones_UI` FREE?
          
            YES, it is!
    
          - Where can I get `Bones_UI`?
          
            See the [project page](https://colossus-services.github.io/bones_ui/){:target="_blank"}.
    
          ''')
        ]);
  }
}

// “组件”路由。
class MyComponents extends UIComponent {
  MyComponents(super.parent);

  @override
  dynamic render() {
    _buildCalendar();

    return [
      '<br><h1>Components</h1>',
      '<hr>',
      UIButton(content, 'UIButton')
        ..onClick.listen((event) => _showAlert('<b>UIButton Clicked:</b>',
            'x: ${event.client.x}<br> y: ${event.client.y}')),
      '<hr>',
      UIInputTable(content, [
        InputConfig('name', 'Name', type: 'text'),
        InputConfig('email', 'Email',
            type: 'email',
            valueNormalizer: (f, v) => v?.toString().trim() ?? ''),
        InputConfig('color', 'Color', type: 'color', optional: true),
        InputConfig('sel', 'Select',
            type: 'select', options: {'a': 'A Option', 'b': 'B Option'}),
      ]),
      '<hr>',
      _uiCalendarPopup,
      '<hr>',
    ];
  }

  UICalendarPopup? _uiCalendarPopup;

  void _buildCalendar() {
    _uiCalendarPopup ??= UICalendarPopup(content,
        backgroundBlur: 4,
        mode: CalendarMode.month,
        allowedModes: {CalendarMode.month, CalendarMode.day},
        currentDate: DateTime(2022, 3, 20),
        events: [
          CalendarEvent.fromJson({
            'title': 'Sleep',
            'initTime': '2022/03/20 01:00',
            'endTime': '2022/03/20 01:30',
          }),
          CalendarEvent('Meeting', DateTime(2022, 3, 20, 9, 0),
              DateTime(2022, 3, 20, 9, 30),
              description: 'Call'),
          CalendarEvent('Lunch', DateTime(2022, 3, 20, 13, 0),
              DateTime(2022, 3, 20, 14, 0),
              description: 'At X'),
          CalendarEvent('Dinner', DateTime(2022, 3, 20, 21, 0),
              DateTime(2022, 3, 20, 21, 40),
              description: 'At Y'),
          CalendarEvent.byDuration(
              'Wine', DateTime(2022, 3, 21, 21, 0), Duration(minutes: 40),
              description: 'Wine and cheese.'),
        ])
      ..onDayClick.listen((day) {
        _uiCalendarPopup!.currentDate = day;
        _uiCalendarPopup!.mode = CalendarMode.day;
      })
      ..onEventClick.listen((event) => window.alert('$event'));
  }

  UIDialogAlert _showAlert(String title, String text) => UIDialogAlert(
      '<div style="background-color: rgba(0,0,0, 0.80); width: 100%; padding: 4px 0;">$title</div><br>$text<br>',
      'OK',
      style:
          'width: 200px; overflow: hidden; border-radius: 8px; padding: 0px 0px 8px 0px; box-shadow: 0 6px 14px rgba(0,0,0, 0.60);')
    ..show();
}

Bootstrap 集成

你可以使用 bones_ui_bootstrap 包来集成 Bootstrap 和 Bones_UI。这将自动处理 JavaScript 库和 CSS 的加载,无需手动添加 HTML 或 JavaScript 代码即可实现完全集成。

单元测试

你可以在你的 Bones_UI 应用中创建单元测试。以下是一个简单的登录测试示例:

@TestOn('browser')
import 'package:bones_ui/bones_ui_test.dart';
import 'package:test/test.dart';

// 导入你的 `UIRoot` 类
import '../web/lib/ui_root.dart'; // ignore: avoid_relative_lib_imports

void main() async {
  group('UIRoot', () {
    late MyUIRoot uiRoot;

    setUpAll(() async {
      // 初始化你的 `UIRoot`:
      uiRoot = await initializeTestUIRoot(MyUIRoot.new);
    });

    tearDownAll(() async {
      await testUISleep(ms: 200);
      uiRoot.clear();
    });

    test('menu: routes', () async {
      await uiRoot.callRenderAndWait();
      await testUISleep(ms: 100);

      var menu = uiRoot.querySelector('.menu');
      expect(menu, isNotNull);

      var routes = menu!.selectAnchorLinksTargets();

      expect(routes,
          unorderedEquals(['register', 'login']));
    });

    test('login', () async {
      await uiRoot.callRenderAndWait();
      await testUISleep(ms: 100);

      var menu = uiRoot.querySelector('.menu');
      expect(menu, isNotNull);

      var linkLogin = menu!
          .selectAnchorElements()
          .firstWhere((e) => e.href?.endsWith('#login') ?? false);

      linkLogin.click();

      await testUISleepUntilRoute('login', timeoutMs: 2000, minMs: 100);

      expectUIRoute('login');

      var navigableContent = uiRoot.querySelector('.navigable-content');

      var inputElements = navigableContent!.selectInputElement();

      var emailInput = inputElements.withID('email').first;
      var passInput = inputElements.withID('password').first;

      emailInput.value = 'admin@mail.com';
      passInput.value = '123456';

      await testUISleep(ms: 100);

      var btnLoginDiv = uiRoot.querySelector('#btn-login');

      btnLoginDiv!.click();
      
      await testUISleepUntilRoutes(['home', ''], timeoutMs: 1000, minMs: 100);

      expectUIRoutes(['home', '']);
    });
  });
}

要运行单元测试,请在项目目录中执行以下命令:

$> bones_ui test

要查看浏览器中运行的测试,请执行以下命令:

$> bones_ui test --show-ui

特性与问题报告

请在 issue tracker 上提交特性请求和 bug 报告。

作者与许可证

通过上述内容,您可以了解如何在 Flutter 项目中使用 Bones_UI 组件库,并提供了一个完整的示例应用。希望这些信息对您有所帮助!

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

1 回复

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


当然,以下是一个关于如何在Flutter项目中使用bones_ui插件的示例代码案例。bones_ui是一个Flutter UI组件库,提供了一系列预构建的UI组件,以便快速构建美观的用户界面。

首先,确保你的Flutter环境已经配置完毕,并且你的项目已经创建。然后,你需要在pubspec.yaml文件中添加bones_ui依赖:

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

添加完依赖后,运行flutter pub get来安装依赖。

接下来,你可以在你的Flutter项目中使用bones_ui提供的组件。以下是一个简单的示例,展示如何使用bones_ui中的一些组件:

import 'package:flutter/material.dart';
import 'package:bones_ui/bones_ui.dart'; // 导入bones_ui包

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

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

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Bones UI Demo'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            // 使用BonesButton组件
            BonesButton(
              title: 'Click Me',
              onPressed: () {
                // 按钮点击事件处理
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text('Button clicked!')),
                );
              },
            ),
            SizedBox(height: 20),
            
            // 使用BonesTextField组件
            BonesTextField(
              labelText: 'Enter your name',
              onChanged: (value) {
                // 文本改变事件处理
                print('Text field value: $value');
              },
            ),
            SizedBox(height: 20),

            // 使用BonesCheckbox组件
            BonesCheckbox(
              value: true, // 初始选中状态
              onChanged: (newValue) {
                // 复选框改变事件处理
                print('Checkbox value: $newValue');
              },
              label: Text('Check me'),
            ),
            SizedBox(height: 20),

            // 使用BonesRadio组件
            Row(
              children: <Widget>[
                BonesRadio<String>(
                  value: 'Option 1',
                  groupValue: 'Option 1', // 当前选中的值
                  onChanged: (newValue) {
                    // 单选按钮改变事件处理
                    print('Radio selected: $newValue');
                  },
                  label: Text('Option 1'),
                ),
                SizedBox(width: 20),
                BonesRadio<String>(
                  value: 'Option 2',
                  groupValue: 'Option 1', // 当前选中的值
                  onChanged: (newValue) {
                    print('Radio selected: $newValue');
                  },
                  label: Text('Option 2'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们展示了如何使用bones_ui提供的BonesButtonBonesTextFieldBonesCheckboxBonesRadio组件。你可以根据自己的需求进一步定制这些组件的样式和行为。

请确保你已经按照bones_ui的文档正确安装和配置了该插件,并查阅其文档以了解更多组件和用法。

回到顶部