Skip to main content

keyline_icons 1.0.0 copy "keyline_icons: ^1.0.0" to clipboard
keyline_icons: ^1.0.0 copied to clipboard

Typed Flutter icons with font and vector APIs for the Keyline collection.

example/lib/main.dart

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:keyline_icons/keyline_icons.dart';

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

class IconGallery extends StatefulWidget {
  const IconGallery({super.key, this.catalog});

  final List<Map<String, dynamic>>? catalog;

  @override
  State<IconGallery> createState() => _IconGalleryState();
}

class _IconGalleryState extends State<IconGallery> {
  late final Future<List<Map<String, dynamic>>> entries =
      widget.catalog == null ? _load() : Future.value(widget.catalog!);
  String query = '';
  double size = 24;
  Color color = Colors.indigo;
  bool dark = false;

  Future<List<Map<String, dynamic>>> _load() async {
    final data = await rootBundle.loadString('assets/icon_catalog.json');
    return (jsonDecode(data) as List).cast<Map<String, dynamic>>();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Keyline Icons',
      themeMode: dark ? ThemeMode.dark : ThemeMode.light,
      theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
      darkTheme: ThemeData.dark(useMaterial3: true),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Keyline Icons'),
          actions: [
            IconButton(
              tooltip: dark ? 'Light theme' : 'Dark theme',
              icon: Icon(dark ? Icons.light_mode : Icons.dark_mode),
              onPressed: () => setState(() => dark = !dark),
            ),
          ],
        ),
        body: FutureBuilder<List<Map<String, dynamic>>>(
          future: entries,
          builder: (context, snapshot) {
            if (snapshot.hasError) {
              return Center(child: Text('Catalog failed: ${snapshot.error}'));
            }
            if (!snapshot.hasData) {
              return const Center(child: CircularProgressIndicator());
            }
            final all = snapshot.data!;
            final filtered =
                all.where((entry) {
                  final needle = query.toLowerCase();
                  return (entry['name'] as String).toLowerCase().contains(
                        needle,
                      ) ||
                      (entry['source'] as String).toLowerCase().contains(
                        needle,
                      );
                }).toList();
            return Column(
              children: [
                Padding(
                  padding: const EdgeInsets.all(12),
                  child: TextField(
                    decoration: InputDecoration(
                      labelText: 'Search ${all.length} icons',
                      prefixIcon: const Icon(Icons.search),
                      border: const OutlineInputBorder(),
                    ),
                    onChanged: (value) => setState(() => query = value),
                  ),
                ),
                Wrap(
                  crossAxisAlignment: WrapCrossAlignment.center,
                  children: [
                    const Text('Size'),
                    SizedBox(
                      width: 180,
                      child: Slider(
                        value: size,
                        min: 16,
                        max: 48,
                        divisions: 16,
                        label: size.round().toString(),
                        onChanged: (value) => setState(() => size = value),
                      ),
                    ),
                    for (final choice in [
                      Colors.indigo,
                      Colors.red,
                      Colors.green,
                      Colors.black,
                    ])
                      IconButton(
                        tooltip:
                            'Choose color ${choice.toARGB32().toRadixString(16)}',
                        onPressed: () => setState(() => color = choice),
                        icon: Icon(Icons.circle, color: choice),
                      ),
                  ],
                ),
                Text('${filtered.length} shown'),
                Expanded(
                  child: LayoutBuilder(
                    builder: (context, constraints) {
                      final columns = (constraints.maxWidth / 180)
                          .floor()
                          .clamp(1, 8);
                      return GridView.builder(
                        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                          crossAxisCount: columns,
                          mainAxisExtent: 180,
                        ),
                        itemCount: filtered.length,
                        itemBuilder: (context, index) {
                          final entry = filtered[index];
                          final name = entry['name'] as String;
                          final source = entry['source'] as String;
                          final codepoint = entry['codepoint'] as int?;
                          final icon =
                              codepoint == null
                                  ? KeyLineVector(
                                    KeyLineVectorAsset(
                                      entry['svg'] as String,
                                      matchTextDirection: entry['rtl'] as bool,
                                    ),
                                    size: size,
                                    color: color,
                                  )
                                  : Icon(
                                    IconData(
                                      // Gallery-only dynamic lookup.
                                      // ignore: non_const_argument_for_const_parameter
                                      codepoint,
                                      fontFamily: 'KeyLineIcon',
                                      fontPackage: 'keyline_icons',
                                      matchTextDirection: entry['rtl'] as bool,
                                    ),
                                    size: size,
                                    color: color,
                                  );
                          final identifier =
                              codepoint == null
                                  ? 'KeyLineVectorIcon.$name'
                                  : 'KeyLineIcon.$name';
                          return Card(
                            child: InkWell(
                              onTap: () {
                                Clipboard.setData(
                                  ClipboardData(text: identifier),
                                );
                                ScaffoldMessenger.of(context).showSnackBar(
                                  SnackBar(content: Text('Copied $identifier')),
                                );
                              },
                              child: Padding(
                                padding: const EdgeInsets.all(8),
                                child: Column(
                                  mainAxisAlignment: MainAxisAlignment.center,
                                  children: [
                                    icon,
                                    const SizedBox(height: 8),
                                    Text(
                                      name,
                                      maxLines: 1,
                                      overflow: TextOverflow.ellipsis,
                                    ),
                                    Text(
                                      identifier,
                                      maxLines: 1,
                                      overflow: TextOverflow.ellipsis,
                                      style:
                                          Theme.of(context).textTheme.bodySmall,
                                    ),
                                    Text(
                                      source,
                                      maxLines: 1,
                                      overflow: TextOverflow.ellipsis,
                                      style:
                                          Theme.of(context).textTheme.bodySmall,
                                    ),
                                    Text(
                                      codepoint == null
                                          ? 'SVG'
                                          : codepoint
                                              .toRadixString(16)
                                              .toUpperCase(),
                                    ),
                                  ],
                                ),
                              ),
                            ),
                          );
                        },
                      );
                    },
                  ),
                ),
              ],
            );
          },
        ),
      ),
    );
  }
}
2
likes
160
points
155
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Typed Flutter icons with font and vector APIs for the Keyline collection.

Homepage
Repository (GitHub)
View/report issues

Topics

#icons #icon-font #svg

License

MIT (license)

Dependencies

flutter, flutter_svg

More

Packages that depend on keyline_icons