Flutter生成趣味头像插件flutter_boring_avatars的使用

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

Flutter生成趣味头像插件flutter_boring_avatars的使用

Logo

Pub Version (including pre-releases)

Features

flutter_boring_avatars 可以基于用户名和颜色调色板生成独特的头像。该项目是 Boring Avatars 的Flutter实现,使用Canvas进行渲染,并添加了过渡动画。

你可以通过 Web Demo 体验效果。

Preview1

Installation

在你的 pubspec.yaml 文件中添加依赖:

dependencies:
  flutter_boring_avatars: any # 或者使用 Pub 上的最新版本

Usage

获取简单头像

BoringAvatar(name: "Maria Mitchell", type: BoringAvatarType.marble);

获取带有过渡动画的头像

AnimatedBoringAvatar(
  name: "Maria Mitchell",
  type: BoringAvatarType.marble,
  duration: const Duration(milliseconds: 300),
)

自定义颜色调色板

final colorPalette = BoringAvatarPalette(Color(0xffA3A948), Color(0xffEDB92E), Color(0xffF85931), Color(0xffCE1836), Color(0xff009989));

BoringAvatar(name: "Maria Mitchell", palette: colorPalette);

设置默认类型和调色板

build(context) {
  return DefaultBoringAvatarStyle(
    type: BoringAvatarType.marble, 
    palette: colorPalette,
    child: Column(
      children: [
        BoringAvatar(name: "Maria Mitchell"),
        BoringAvatar(name: "Alexis Brooks"),
        BoringAvatar(name: "Justin Gray"),
      ]
    ),
  );
}

使用ShapeBorder控制头像形状并添加边框

BoringAvatar(
  name: "Maria Mitchell",
  type: BoringAvatarType.marble,
  shape: OvalBorder(), // 或者使用 RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))
);

作为装饰使用头像,支持在AnimatedContainer中的过渡动画

Container(
  decoration: BoringAvatarDecoration(
    avatarData: BoringAvatarData.generate(name: name),
  ),
);

导出头像为图片

final avatarData = BoringAvatarData.generate(name: name);
final image = await avatarData.toImage(size: const Size.square(256));
final pngByteData = await image.toByteData(format: ImageByteFormat.png);

示例代码

下面是一个完整的示例应用,展示了如何使用 flutter_boring_avatars 插件创建一个可以交互的头像生成器。

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_boring_avatars/flutter_boring_avatars.dart';

void main() {
  runApp(const MaterialApp(home: MyApp()));
}

class AvatarInputWidget extends StatefulWidget {
  const AvatarInputWidget({
    Key? key,
    required this.name,
    required this.onNameChanged,
    required this.resetInput,
    required this.shape,
  }) : super(key: key);

  final String name;
  final ValueChanged<String> onNameChanged;
  final int resetInput;
  final ShapeBorder shape;

  @override
  State<AvatarInputWidget> createState() => _AvatarInputWidgetState();
}

class _AvatarInputWidgetState extends State<AvatarInputWidget> {
  late TextEditingController textController = TextEditingController(
    text: widget.name,
  );

  late int inputKey = widget.resetInput;

  copyImage() async {
    final type = DefaultBoringAvatarType.maybeOf(context)?.type ??
        BoringAvatarType.marble;
    final colorPalette = DefaultBoringAvatarPalette.maybeOf(context)?.palette ??
        BoringAvatarPalette.defaultPalette;

    final image = await BoringAvatarData.generate(
      name: widget.name,
      type: type,
      palette: colorPalette,
      // shape: widget.shape,
    ).toImage(
      size: const Size.square(512),
    );

    final pngData = await image.toByteData(format: ImageByteFormat.png);

    final clipboard = SystemClipboard.instance;
    if (clipboard == null) {
      return; // Clipboard API is not supported on this platform.
    }
    final item = DataWriterItem();
    item.add(Formats.png(pngData!.buffer.asUint8List()));
    await clipboard.write([item]);
    ScaffoldMessenger.of(context).clearSnackBars();
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(
        content: Text('Avatar image copied to clipboard'),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    if (widget.resetInput != inputKey) {
      textController = TextEditingController(
        text: widget.name,
      );
      inputKey = widget.resetInput;
    }
    return Column(
      children: [
        Expanded(
          child: AnimatedBoringAvatar(
            duration: const Duration(milliseconds: 1200),
            curve: Curves.easeInOutCubicEmphasized,
            name: widget.name,
            shape: widget.shape,
            child: RawMaterialButton(
              shape: widget.shape,
              onPressed: copyImage,
            ),
          ),
        ),
        const SizedBox(height: 8),
        SizedBox(
          height: 24,
          child: TextField(
            decoration: InputDecoration(
              contentPadding: const EdgeInsets.symmetric(horizontal: 8),
              focusedBorder: const OutlineInputBorder(
                borderSide: BorderSide(color: Colors.black, width: 1),
                borderRadius: BorderRadius.all(Radius.circular(32)),
              ),
              enabledBorder: OutlineInputBorder(
                borderSide:
                    BorderSide(color: Colors.grey.withOpacity(0.0), width: 1),
                borderRadius: const BorderRadius.all(Radius.circular(32)),
              ),
            ),
            controller: textController,
            style: const TextStyle(fontSize: 12),
            textAlign: TextAlign.center,
            onChanged: widget.onNameChanged,
          ),
        ),
        const SizedBox(height: 8),
      ],
    );
  }
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
  String name = 'Bessie Coleman';
  TextEditingController textController = TextEditingController.fromValue(
      const TextEditingValue(text: 'Bessie Coleman'));

  BoringAvatarType type = BoringAvatarType.beam;

  RandomNames randomNames = RandomNames(Zone.us);

  late BoringAvatarPalette colorPalette;
  late List<String> names;

  ShapeBorder shape = const OvalBorder();

  @override
  void initState() {
    colorPalette = BoringAvatarPalette(getRandomColors());
    names = List.generate(200, (index) => randomNames.fullName());
    super.initState();
  }

  avatarItem(int index) {
    final name = names[index];
    return AvatarInputWidget(
        name: name,
        resetInput: names.hashCode,
        shape: shape,
        onNameChanged: (name) {
          setState(() {
            names[index] = name;
          });
        });
  }

  goToTestPage() {
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => TestPage()),
    );
  }

  @override
  Widget build(BuildContext context) {
    final colorScheme =
        ColorScheme.fromSeed(seedColor: colorPalette.getColor(2));
    return MaterialApp(
      theme: ThemeData.from(colorScheme: colorScheme),
      home: Scaffold(
        appBar: AppBar(
          centerTitle: true,
          title: GestureDetector(
            child: Text('Boring Avatars'),
            onTap: goToTestPage,
          ),
          actions: [
            IconButton(
              tooltip: 'Pub.dev',
              onPressed: () async {
                final uri = Uri.parse(
                    'https://pub.dev/packages/flutter_boring_avatars');
                if (!await launchUrl(uri, webOnlyWindowName: '_blank')) {
                  throw Exception('Could not launch $uri');
                }
              },
              icon: const Icon(Icons.brand_flutter),
            ),
            IconButton(
              tooltip: 'Github',
              onPressed: () async {
                final uri = Uri.parse(
                    'https://github.com/xioxin/flutter_boring_avatars');
                if (!await launchUrl(uri, webOnlyWindowName: '_blank')) {
                  throw Exception('Could not launch $uri');
                }
              },
              icon: const Icon(Icons.brand_github),
            ),
            IconButton(
              tooltip: 'Randomize All',
              onPressed: () {
                setState(() {
                  names =
                      List.generate(2000, (index) => randomNames.fullName());
                  colorPalette = BoringAvatarPalette(getRandomColors());
                });
              },
              icon: const Icon(Icons.refresh),
            ),
          ],
        ),
        body: Stack(
          children: [
            DefaultBoringAvatarStyle(
              type: type,
              palette: colorPalette,
              child: Positioned.fill(
                child: GridView.builder(
                  padding: const EdgeInsets.only(
                      left: 32, top: 140, right: 32, bottom: 32),
                  gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
                    maxCrossAxisExtent: 140,
                    crossAxisSpacing: 16,
                    mainAxisSpacing: 16,
                  ),
                  itemCount: names.length,
                  itemBuilder: (context, index) {
                    return avatarItem(index);
                  },
                ),
              ),
            ),
            Positioned(
              top: 0,
              left: 16,
              right: 16,
              child: ControlBarWidget(
                type: type,
                palette: colorPalette,
                shape: shape,
                onShapeChanged: (v) {
                  setState(() {
                    shape = v;
                  });
                },
                onTypeChanged: (v) {
                  setState(() {
                    type = v;
                  });
                },
                onPaletteChanged: (v) {
                  setState(() {
                    colorPalette = v;
                  });
                },
                onRandomizeNames: () {
                  setState(() {
                    names =
                        List.generate(2000, (index) => randomNames.fullName());
                  });
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

这个示例应用展示了如何使用 flutter_boring_avatars 创建一个可以自定义和导出的趣味头像生成器。你可以根据需要调整和扩展此代码以满足特定需求。


更多关于Flutter生成趣味头像插件flutter_boring_avatars的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter生成趣味头像插件flutter_boring_avatars的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用flutter_boring_avatars插件来生成趣味头像的示例代码。这个插件允许你基于一些简单的参数生成有趣的、独一无二的头像。

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

dependencies:
  flutter:
    sdk: flutter
  flutter_boring_avatars: ^x.y.z  # 替换为最新版本号

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

接下来,在你的Dart文件中,你可以按照以下方式使用flutter_boring_avatars

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

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

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

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 配置生成头像的参数
    final avatarParams = BoringAvatarParams(
      text: 'AB12', // 文本可以是任何字符串,用于生成头像的一部分
      backgroundColor: Colors.grey[300]!, // 背景颜色
      foregroundColor: Colors.blue, // 前景颜色(通常是文本颜色)
      shape: BoringAvatarShape.circle, // 头像形状(圆形或方形)
      borderRadius: 10.0, // 如果形状是方形,可以设置圆角半径
      size: 100.0, // 头像大小
    );

    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Boring Avatars Demo'),
      ),
      body: Center(
        child: BoringAvatar(
          params: avatarParams,
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,其中包含一个使用BoringAvatar生成的趣味头像。BoringAvatarParams类允许我们自定义头像的多个方面,包括文本、背景颜色、前景颜色、形状、圆角半径和大小。

你可以根据需要调整avatarParams中的参数来生成不同的头像。text参数通常是一个简短的字符串,它会被用来生成头像的一部分图案。

这个插件非常适合在需要快速生成独特头像的应用中使用,比如用户头像生成器、游戏角色创建工具等。

回到顶部