关于程序员:Flutter-中使用-Widgetbook-管理你的组件

37次阅读

共计 8210 个字符,预计需要花费 21 分钟才能阅读完成。

Flutter 中应用 Widgetbook 治理你的组件

<img src=”https://ducafecat.oss-cn-beijing.aliyuncs.com/podcast/2023/05/ad04470d9d20876ab34c1b36b08cf482.jpeg” style=”width:90%;” />

前言

Flutter 界面开发中咱们有几个痛点:

  • 与设计师合作复用一套设计规范(figma)
  • 可视化的治理你的组件代码(根底组件、业务组件)
  • 不同设施尺寸测试你的组件
  • 实时批改你的测试组件参数

原文 https://ducafecat.com/blog/flutter-uses-a-widgetbook-to-manag…

视频

https://www.bilibili.com/video/BV1qM4y1b7WL/

参考

  • https://docs.widgetbook.io
  • https://ant.design/docs/spec/introduce-cn

Widgetbook

https://www.widgetbook.io/

<img src=”https://ducafecat.oss-cn-beijing.aliyuncs.com/podcast/2023/05/6840ba9ac214ee1f2938c79ed5fab5a6.png” style=”width:80%;” />

Flutter Widgetbook 是一个用于构建和交互 Flutter 组件库的工具。它容许您在独自的应用程序中构建和演示您的 Flutter 组件,以便您能够在不运行残缺应用程序的状况下进行疾速迭代和测试。

应用 Flutter Widgetbook,您能够:

  • 构建和演示单个组件,而无需在残缺应用程序中运行它们。
  • 以交互方式测试组件的不同状态和属性,以及不同平台和设施的外观和行为。
  • 共享您的组件库,并让其他人轻松地查看和测试您的组件。

您能够在 Flutter 应用程序中应用 Widgetbook,也能够将其作为独立应用程序应用。在 Widgetbook 中,您能够编写 Dart 代码来定义组件和演示它们的用法。您能够应用 Flutter 提供的任何组件和库,并应用 Widgetbook 提供的一些工具来组织和显示您的组件。

设计规范

前端设计规范是一组定义前端设计和开发过程中所需恪守的准则和规定的标准。它们旨在确保前端代码的一致性、可维护性、可扩展性和可重用性,并促成团队间的合作。

前端设计规范次要包含以下内容:

  1. 布局标准:定义页面布局和排版的规定,包含网格零碎、排版间距、基准线等。
  2. 款式标准:定义色彩、字体、图标、按钮等根本款式的应用和标准,包含设计格调、调色板、字体类型、字号、行低等。
  3. 组件标准:定义前端组件的设计和开发规定,包含组件的命名、构造、款式、交互、状态治理等。
  4. 图片和媒体标准:定义图片和媒体资源的格局、尺寸、优化和加载等规定,以进步页面性能和用户体验。
  5. 响应式设计规范:定义响应式设计的准则和规定,包含页面布局、元素大小和地位、字体大小、图片和媒体资源的显示等。
  6. 可拜访性标准:定义网站或应用程序的可拜访性规定,包含键盘导航、语义标记、焦点批示、色彩对比度等。
  7. 性能标准:定义优化前端性能的规定,包含代码压缩、缓存管制、资源加载、代码宰割等。

前端设计规范能够通过文档、工具、模板和代码库等形式来实现和保护。它们能够帮忙团队进步开发效率、升高保护老本、放弃代码品质和可维护性,并促成设计和开发间的合作。

Ant Design 设计规范参考

https://ant.design/docs/spec/introduce-cn

<img src=”https://ducafecat.oss-cn-beijing.aliyuncs.com/podcast/2023/05/54cef4391c97f0e4b4820b0b9b1c4232.png” style=”width:80%;” />

代码

https://github.com/ducafecat/flutter_develop_tips/tree/main/flutter-widgetbook

步骤

装置 widgetbook 组件

pubspec.yaml

dev_dependencies:
  flutter_test:
    sdk: flutter

  ...

  widgetbook: ^3.0.0-beta.14

留神是放在 dev_dependencies 节点上面

编写调试界面

lib/app.widgetbook.dart

// ignore_for_file: depend_on_referenced_packages

import 'package:flutter/material.dart';
import 'package:widgetbook/widgetbook.dart';
import 'package:widgetbook_in_flutter_course/widgets/button.dart';

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

class HotReload extends StatelessWidget {const HotReload({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Widgetbook.material(addons: [],
      directories: [],);
  }
}

运行

<img src=”https://ducafecat.oss-cn-beijing.aliyuncs.com/podcast/2023/05/d62fe3adfa8a115e710f6595995abcd8.png” style=”width:80%;” />

退出组件

筹备两个组件代码

lib/widgets/button.dart

import 'package:flutter/material.dart';

class MyElevatedButton extends StatelessWidget {
  final VoidCallback? onPressed;
  final String? text;
  final IconData? icon;
  final Color? textColor;
  final Color? buttonColor;
  final double? borderRadius;
  final double? height;
  final double? width;

  const MyElevatedButton({
    Key? key,
    this.onPressed,
    this.text,
    this.icon,
    this.textColor,
    this.buttonColor,
    this.borderRadius,
    this.height,
    this.width,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: height ?? 48.0,
      width: width,
      child: ElevatedButton(
        onPressed: onPressed,
        style: ElevatedButton.styleFrom(primary: buttonColor ?? Theme.of(context).primaryColor,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(borderRadius ?? 8.0),
          ),
        ),
        child: icon == null
            ? Text(
                text!,
                style: TextStyle(color: textColor ?? Colors.white),
              )
            : Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [Icon(icon, color: textColor ?? Colors.white),
                  SizedBox(width: 8.0),
                  Text(
                    text!,
                    style: TextStyle(color: textColor ?? Colors.white),
                  ),
                ],
              ),
      ),
    );
  }
}

lib/components/login.dart

import 'package:flutter/material.dart';

class LoginForm extends StatefulWidget {const LoginForm({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<LoginForm> createState() => _LoginFormState();
}

class _LoginFormState extends State<LoginForm> {final _formKey = GlobalKey<FormState>();
  final _emailController = TextEditingController();
  final _passwordController = TextEditingController();

  bool _isObscured = true;
  bool _isLoading = false;

  void _toggleObscure() {setState(() {_isObscured = !_isObscured;});
  }

  void _submit() async {if (_formKey.currentState!.validate()) {setState(() {_isLoading = true;});

      // Simulate a login request
      await Future.delayed(const Duration(seconds: 2));

      setState(() {_isLoading = false;});

      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Logged in successfully!'),
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(appBar: AppBar(title: Text(widget.title)),
      body: Form(
        key: _formKey,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            TextFormField(
              controller: _emailController,
              keyboardType: TextInputType.emailAddress,
              decoration: const InputDecoration(labelText: 'Email',),
              validator: (value) {if (value == null || value.isEmpty) {return 'Please enter your email';}
                if (!value.contains('@')) {return 'Please enter a valid email address';}
                return null;
              },
            ),
            const SizedBox(height: 16),
            TextFormField(
              controller: _passwordController,
              obscureText: _isObscured,
              decoration: InputDecoration(
                labelText: 'Password',
                suffixIcon: IconButton(
                  icon: Icon(_isObscured ? Icons.visibility : Icons.visibility_off,),
                  onPressed: _toggleObscure,
                ),
              ),
              validator: (value) {if (value == null || value.isEmpty) {return 'Please enter your password';}
                if (value.length < 6) {return 'Password must be at least 6 characters long';}
                return null;
              },
            ),
            const SizedBox(height: 32),
            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: _isLoading ? null : _submit,
                child: _isLoading
                    ? const CircularProgressIndicator()
                    : const Text('Log in'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

分类 1 – 公共组件

  WidgetbookCategory buildWidgetbookCategory() {
    return WidgetbookCategory(
      name: '公共组件',
      children: [
        WidgetbookComponent(
          name: '按钮',
          useCases: [
            WidgetbookUseCase.center(
              name: "红色背景",
              child: MyElevatedButton(onPressed: () => print("Button pressed"),
                text: "Click me",
                icon: Icons.arrow_forward,
                buttonColor: Colors.red,
                borderRadius: 16.0,
                height: 60.0,
                width: double.infinity,
              ),
            ),
            WidgetbookUseCase.center(
              name: "蓝色色背景",
              child: MyElevatedButton(onPressed: () => print("Button pressed"),
                text: "Click me",
                icon: Icons.arrow_forward,
                buttonColor: Colors.blue,
                borderRadius: 16.0,
                height: 60.0,
                width: double.infinity,
              ),
            )
          ],
        ),
      ],
    );
  }

分类 2 – 业务组件

  WidgetbookCategory buildWidgetbookCategory2() {
    return WidgetbookCategory(
      name: '业务组件',
      children: [
        WidgetbookComponent(
          name: '零碎罕用',
          useCases: [
            WidgetbookUseCase(
              name: "登录界面",
              builder: (BuildContext context) {
                return LoginForm(
                  title: context.knobs.text(label: '题目 [title]',
                    initialValue: '用户登录',
                  ),
                );
              },
            ),
            WidgetbookUseCase(
              name: "注册界面",
              builder: (BuildContext context) {
                return LoginForm(
                  title: context.knobs.text(label: '题目 [title]',
                    initialValue: '用户注册',
                  ),
                );
              },
            ),
          ],
        ),
      ],
    );
  }

通过 knobs 的形式设置调试参数

其它参数类型

<img src=”https://ducafecat.oss-cn-beijing.aliyuncs.com/podcast/2023/05/b46521f4ef5e8504fecc8286ed5108de.png” style=”width:80%;” />

build 函数

  @override
  Widget build(BuildContext context) {
    return Widgetbook.material(addons: [],
      directories: [
        // 根底组件
        buildWidgetbookCategory(),

        // 业务组件
        buildWidgetbookCategory2(),],
    );
  }

输入

<img src=”https://ducafecat.oss-cn-beijing.aliyuncs.com/podcast/2023/05/b5a12d8974454de0967b9dd2b47aa459.png” style=”width:80%;” />

设置选项

主题

  MaterialThemeAddon buildMaterialThemeAddon() {
    return MaterialThemeAddon(
        setting: MaterialThemeSetting.firstAsSelected(themes: [WidgetbookTheme(name: "dark", data: ThemeData.dark()),
      WidgetbookTheme(name: "light", data: ThemeData.light()),
    ]));
  }

字体尺寸

  TextScaleAddon buildTextScaleAddon() {
    return TextScaleAddon(
        setting: TextScaleSetting.firstAsSelected(textScales: [1.0, 1.25, 1.5, 1.75, 2]));
  }

build 函数

  @override
  Widget build(BuildContext context) {
    return Widgetbook.material(
      addons: [
        // 主题
        buildMaterialThemeAddon(),

        // 字体大小
        buildTextScaleAddon(),],
      directories: [
        // 根底组件
        buildWidgetbookCategory(),

        // 业务组件
        buildWidgetbookCategory2(),],
    );
  }

输入

<img src=”https://ducafecat.oss-cn-beijing.aliyuncs.com/podcast/2023/05/39a52b9f8a7377520be5e5188d80725c.png” style=”width:80%;” />

其它 addon

<img src=”https://ducafecat.oss-cn-beijing.aliyuncs.com/podcast/2023/05/6904961f63b178731a9fe2d567769ace.png” style=”width:50%;” />

小结

Flutter Widgetbook 对前端开发工作有以下益处:

  1. 进步开发效率:Flutter Widgetbook 能够让前端开发人员在不须要启动残缺应用程序的状况下构建和演示 Flutter 组件,疾速迭代和测试组件的不同状态和属性,从而进步开发效率。
  2. 促成组件复用:Flutter Widgetbook 能够让前端开发人员在独自的应用程序中构建和演示组件,从而促成组件的复用和共享,缩小代码反复和保护老本。
  3. 放弃代码一致性:Flutter Widgetbook 能够作为一个组件库来应用,定义前端组件的设计和开发规定,从而放弃代码的一致性、可维护性、可扩展性和可重用性。
  4. 进步跨团队合作:Flutter Widgetbook 能够让前端开发人员共享他们的组件库,并让其他人轻松地查看和测试他们的组件,从而促成跨团队合作和常识共享。
  5. 进步用户体验:Flutter Widgetbook 能够让前端开发人员在不同平台和设施上测试组件的外观和行为,以确保它们可能提供统一的用户体验,从而进步用户体验和用户满意度。

总之,Flutter Widgetbook 是一个有用的工具,能够帮忙前端开发人员更轻松地构建和测试 Flutter 组件,从而进步开发效率、放弃代码品质和可维护性,并促成跨团队合作和常识共享,最终进步用户体验和用户满意度。


© 猫哥

ducafecat.com

end

本文由 mdnice 多平台公布

正文完
 0