ms_undraw 4.1.1 copy "ms_undraw: ^4.1.1" to clipboard
ms_undraw: ^4.1.1 copied to clipboard

Updated UnDraw library. These illustrations are designed by Katerina Limpitsouni.

example/lib/main.dart

import 'dart:async';
import 'dart:math';

import 'package:context_menus/context_menus.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:ms_undraw/ms_undraw.dart';
import 'package:url_launcher/url_launcher_string.dart';

void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MS Undraw - Demo',
      theme: ThemeData(
        primarySwatch: Colors.red,
        secondaryHeaderColor: Colors.orangeAccent,
        useMaterial3: false,
      ),
      home: ContextMenuOverlay(
        child: MyHomePage(title: "${UnDrawIllustration.values.length} Illustrations"),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Color color = Colors.red;
  UnDrawIllustration illustration = UnDrawIllustration.mobile_application;
  Timer? timer;
  final List<UnDrawIllustration> _filtered = [];
  final TextEditingController _controller = TextEditingController();
  final FocusNode _focus = FocusNode();

  @override
  Widget build(BuildContext context) {
    final width = MediaQuery.of(context).size.width;
    final double horizontalPadding = width < 1024 ? 16 : (width - 1024) / 2;
    final double maxCrossAxisCount = max(1, min(4, width / 300));

    return FocusScope(
      autofocus: true,
      onKey: (node, event) {
        if (event is RawKeyDownEvent && event.logicalKey == LogicalKeyboardKey.escape) {
          _focus.unfocus();
          _controller.clear();
          _filtered.clear();
          setState(() {});
          return KeyEventResult.handled;
        }
        if (event is RawKeyDownEvent && event.logicalKey == LogicalKeyboardKey.slash && !_focus.hasFocus) {
          _focus.requestFocus();
          return KeyEventResult.handled;
        }
        return KeyEventResult.ignored;
      },
      child: Scaffold(
        backgroundColor: Colors.white,
        appBar: AppBar(
          actions: [
            IconButton(
              onPressed: () {
                showAboutDialog(
                  context: context,
                  children: [
                    TextButton(
                      onPressed: () => launchUrlString("https://pub.flutter-io.cn/packages/ms_undraw"),
                      child: const Text('https://pub.flutter-io.cn/packages/ms_undraw'),
                    ),
                    TextButton(
                      onPressed: () => launchUrlString("https://undraw.co/"),
                      child: const Text('https://undraw.co/'),
                    ),
                  ],
                );
              },
              icon: const Icon(Icons.info),
            ),
          ],
          title: TextFormField(
            controller: _controller,
            focusNode: _focus,
            onChanged: (s) {
              timer?.cancel();
              timer = Timer(const Duration(seconds: 1), () {
                setState(() {
                  _filtered.clear();
                  if (s.isNotEmpty) {
                    _filtered.addAll(UnDrawIllustration.values.where((element) =>
                        _changeName(element.name).toLowerCase().contains(s.toLowerCase())));
                  }
                });
              });
            },
            onFieldSubmitted: (s) {
              timer?.cancel();
              _filtered.clear();
              _focus.requestFocus();
              if (s.isNotEmpty) {
                setState(() {
                  _filtered.addAll(UnDrawIllustration.values.where((element) =>
                      _changeName(element.name).toLowerCase().contains(s.toLowerCase())));
                });
              }
            },
            cursorColor: Colors.white,
            style: const TextStyle(color: Colors.white),
            decoration: InputDecoration(
              label: Text("Type to search"),
              icon: Icon(Icons.search, color: Colors.white),
              isDense: true,
              iconColor: Colors.white,
              focusColor: Colors.white,
              prefixIconColor: Colors.white,
              labelStyle: TextStyle(color: Colors.white),
              suffixIconColor: Colors.white,
              suffixIcon: IconButton(
                onPressed: () {
                  setState(() {
                    _filtered.clear();
                    _controller.clear();
                  });
                },
                icon: const Icon(Icons.clear, color: Colors.white),
              ),
            ),
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            setState(() {
              color = Color(0xFF000000 | Random().nextInt(0xFFFFFF));
            });
          },
          backgroundColor: Colors.red,
          child: const Icon(Icons.color_lens),
        ),
        body: GridView.builder(
          padding: EdgeInsets.only(top: 16, left: horizontalPadding, right: horizontalPadding, bottom: 64),
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: maxCrossAxisCount.toInt(),
            crossAxisSpacing: 16,
            mainAxisSpacing: 16,
            childAspectRatio: 0.72, // Adjust for visual balance (width / height)
          ),
          itemCount: _filtered.isEmpty ? UnDrawIllustration.values.length : _filtered.length,
          itemBuilder: (_, index) {
            final undraw = _filtered.isEmpty ? UnDrawIllustration.values[index] : _filtered[index];
            return ContextMenuRegion(
              contextMenu: GenericContextMenu(
                buttonConfigs: [
                  ContextMenuButtonConfig("Copy name", onPressed: () => _copyName(undraw)),
                  ContextMenuButtonConfig("Copy widget code", onPressed: () => _copyCode(undraw)),
                ],
              ),
              child: LayoutBuilder(
                builder: (context, constraints) {
                  return Container(
                    decoration: BoxDecoration(
                      color: Colors.grey.shade100,
                      border: Border.all(color: Colors.grey.shade300),
                      borderRadius: const BorderRadius.all(Radius.circular(16)),
                      boxShadow: const [
                        BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 4)),
                      ],
                    ),
                    padding: const EdgeInsets.all(16.0), // Safer padding
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        Text(_changeName(undraw.name)),
                        const SizedBox(height: 12),
                        Expanded(
                          child: Center(
                            child: UnDraw(
                              color: color,
                              useMemCache: false,
                              height: constraints.maxHeight * 0.5,
                              // Dynamic sizing
                              illustration: undraw,
                              placeholder: const Text("Illustration is loading..."),
                              errorWidget: const Icon(Icons.error_outline, color: Colors.red, size: 50),
                            ),
                          ),
                        ),
                        const Divider(),
                        Row(
                          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                          children: [
                            TextButton.icon(
                              onPressed: () => _copyName(undraw),
                              icon: const Icon(Icons.copy),
                              label: const Text("Copy name"),
                            ),
                            TextButton.icon(
                              onPressed: () => _copyCode(undraw),
                              icon: const Icon(Icons.code),
                              label: const Text("Copy Widget code"),
                            ),
                          ],
                        ),
                      ],
                    ),
                  );
                },
              ),
            );
          },
        ),
      ),
    );
  }

  String _changeName(String name) {
    return name
        .replaceAll('_', ' ')
        .trim()
        .split(' ')
        .map((e) => "${e[0].toUpperCase()}${e.substring(1).toLowerCase()}")
        .toList()
        .join(' ')
        .trim();
  }

  void _copyName(UnDrawIllustration undraw) {
    final text = "UnDrawIllustration.${undraw.name}";
    Clipboard.setData(ClipboardData(text: text));
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Copied name: $text')),
    );
  }

  void _copyCode(UnDrawIllustration undraw) {
    final code = """UnDraw(
    color: Theme.of(context).primaryColor,
    illustration: UnDrawIllustration.${undraw.name},
  )""";
    Clipboard.setData(ClipboardData(text: code));
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Copied widget code for: ${undraw.name}')),
    );
  }
}
62
likes
150
points
638
downloads

Publisher

verified publishermarcussoftware.info

Weekly Downloads

Updated UnDraw library. These illustrations are designed by Katerina Limpitsouni.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter, flutter_svg, http, path_provider

More

Packages that depend on ms_undraw