flutterx_material_tool 1.1.0 copy "flutterx_material_tool: ^1.1.0" to clipboard
flutterx_material_tool: ^1.1.0 copied to clipboard

Reverse engineered Google material tool allows to generate material palette from primary color

example/lib/main.dart

import 'package:example/dialog_about.dart';
import 'package:example/dialog_export.dart';
import 'package:example/shades_indicator.dart';
import 'package:example/shades_palette.dart';
import 'package:example/shades_title.dart';
import 'package:flutter/material.dart' hide DialogRoute, AboutDialog;
import 'package:flutter/services.dart';
import 'package:flutterx_application/flutterx_application.dart';
import 'package:flutterx_color_picker/flutterx_color_picker.dart';
import 'package:flutterx_live_data/flutterx_live_data.dart';
import 'package:flutterx_material_tool/flutterx_material_tool.dart';
import 'package:flutterx_utils/flutterx_utils.dart';

void main() => runMaterialApp(
    name: 'Flutterx Material Tool Demo',
    routes: {MaterialToolExample.route},
    materialAppFactory: (key, initialRoute, onGenerateRoute, title, locale, localizationsDelegates, supportedLocales) {
      final primary = MutableLiveData<Color>(initialValue: Colors.grey);
      return AppColor(
          primary: primary,
          child: LiveDataBuilder<Color>(
              data: primary,
              builder: (context, value) => MaterialApp(
                  key: key,
                  initialRoute: initialRoute,
                  onGenerateRoute: onGenerateRoute,
                  title: title,
                  theme: ThemeData.from(
                          colorScheme: generateScheme(
                              primary: value.withOpacity(1), secondaryFromPrimary: MaterialVariant.primary))
                      .let((theme) => theme.copyWith(
                            textTheme: theme.textTheme.apply(fontFamily: 'RobotoMono'),
                            primaryTextTheme: theme.primaryTextTheme.apply(fontFamily: 'RobotoMono'),
                          )),
                  locale: locale,
                  localizationsDelegates: localizationsDelegates,
                  supportedLocales: supportedLocales,
                  debugShowCheckedModeBanner: false)));
    });

class AppColor extends InheritedWidget {
  final MutableLiveData<Color> primary;

  const AppColor({Key? key, required this.primary, required Widget child}) : super(key: key, child: child);

  static AppColor of(BuildContext context) => requireNotNull(context.dependOnInheritedWidgetOfExactType<AppColor>());

  @override
  bool updateShouldNotify(AppColor oldWidget) => oldWidget.primary != primary;
}

class MaterialToolExample extends StatefulWidget {
  static final ActivityRoute<void> route =
      ActivityRoute.builder(MaterialToolExample, builder: (context, args) => const MaterialToolExample());

  const MaterialToolExample({Key? key}) : super(key: key);

  @override
  State<MaterialToolExample> createState() => _MaterialToolExampleState();
}

class _MaterialToolExampleState extends State<MaterialToolExample> {
  final MaterialPalette<ColorSwatch<int>> _palette = MaterialPalette.primary;
  late MutableLiveData<Color> _primary;

  @override
  Widget build(BuildContext context) {
    _primary = AppColor.of(context).primary;
    final actions = <Pair<String, IconData>, VoidCallback>{
      const Pair('Reset', Icons.restore): _actionRestore,
      const Pair('Copy', Icons.copy): _actionCopy,
      const Pair('Paste', Icons.paste): _actionPaste,
      const Pair('Export', Icons.reply): _actionExport,
      const Pair('About', Icons.info_outline): _actionAbout,
    };
    return Scaffold(
        appBar: AppBar(
            title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
              const Text('Material Tool'),
              LiveDataBuilder<Color>(
                  data: _primary,
                  builder: (context, value) {
                    final affinity = findAffinity(value);
                    return MaterialStatefulWidget(
                        onPressed: () => _primary.value = affinity.color,
                        builder: (context, states, child) => Text(
                            '#${affinity.src.hexString.substring(2)} ${affinity.exact ? '' : '~'}${affinity.name}',
                            overflow: TextOverflow.ellipsis,
                            maxLines: 1,
                            style: DefaultTextStyle.of(context).style.copyWith(
                                fontSize: 12,
                                decoration: states.pressed || states.hovered ? TextDecoration.underline : null)));
                  }),
            ]),
            actions: [
              Builder(builder: (context) {
                final onPrimary = DefaultTextStyle.of(context).style.color;
                return PopupMenuButton<VoidCallback>(
                    icon: Icon(Icons.more_vert, color: onPrimary),
                    onSelected: (callback) => callback(),
                    itemBuilder: (context) => actions.entries
                        .map((entry) => PopupMenuItem<VoidCallback>(
                            value: entry.value,
                            child: Row(children: [
                              Builder(builder: (context) {
                                final iconColor = DefaultTextStyle.of(context).style.color;
                                return Icon(entry.key.second, color: iconColor, size: 20);
                              }),
                              const SizedBox(width: 8),
                              Text(entry.key.first)
                            ])))
                        .toList(growable: false));
              }),
            ]),
        body: SingleChildScrollView(
            child: MediatorLiveDataBuilder<Color, RGBColor>(
                data: _primary, transform: (color) => color.rgb, builder: _buildPalettes)),
        floatingActionButton: FloatingActionButton(
            onPressed: () async {
              final result = await ColorPicker.open(
                  tracks: const {TrackType.hue, TrackType.saturation, TrackType.lightness},
                  context: context,
                  initialValue: _primary.value);
              if (result != null) _primary.value = result;
            },
            child: const Icon(Icons.palette_outlined)));
  }

  Widget _buildPalettes(BuildContext context, RGBColor color) =>
      Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [
        ShadesTitle(shades: _palette.shades, title: 'PRIMARY'),
        ShadesPalette(palette: _palette, variant: MaterialVariant.primary, input: color, onColor: _onColor),
        ShadesIndicator(shades: _palette.shades),
        ShadesTitle(shades: _palette.shades, title: 'COMPLEMENTARY'),
        ShadesPalette(palette: _palette, variant: MaterialVariant.complementary, input: color, onColor: _onColor),
        ShadesIndicator(shades: _palette.shades),
        ShadesTitle(shades: _palette.shades, title: 'ANALOGOUS'),
        ShadesPalette(palette: _palette, variant: MaterialVariant.analogous_a, input: color, onColor: _onColor),
        ShadesPalette(palette: _palette, variant: MaterialVariant.analogous_b, input: color, onColor: _onColor),
        ShadesIndicator(shades: _palette.shades),
        ShadesTitle(shades: _palette.shades, title: 'TRIADIC'),
        ShadesPalette(palette: _palette, variant: MaterialVariant.triadic_a, input: color, onColor: _onColor),
        ShadesPalette(palette: _palette, variant: MaterialVariant.triadic_b, input: color, onColor: _onColor),
        ShadesIndicator(shades: _palette.shades),
        const SizedBox(height: 86),
      ]);

  void _onColor(MaterialVariant variant, PaletteData data, int index) =>
      _primary.value = variant.remove(data.swatch[index].rgb).toColor();

  void _actionExport() => ExportDialog.open(context, _palette, _primary.value.rgb);

  void _actionRestore() {
    _primary.value = Colors.grey;
    _showSnackBar('Colors reset done');
  }

  void _actionCopy() {
    final color = _primary.value;
    Clipboard.setData(ClipboardData(text: '#${color.hexString}'));
    _showSnackBar('Copied to clipboard: #${color.hexString.substring(2)}', color: color);
  }

  Future<void> _actionPaste() async {
    final data = await Clipboard.getData(Clipboard.kTextPlain);
    final value = int.tryParse(data?.text?.replaceAll('#', '') ?? '', radix: 16);
    if (value == null) {
      _showSnackBar('Clipboard data is not a color');
      return;
    }
    final color = _primary.value = Color(value);
    _showSnackBar('Pasted from clipboard: #${color.hexString.substring(2)}', color: color);
  }

  void _actionAbout() => AboutDialog.open(context);

  void _showSnackBar(String message, {Color? color}) {
    final messenger = ScaffoldMessenger.of(context);
    messenger.hideCurrentSnackBar();
    messenger.showSnackBar(SnackBar(
        content: Text(message,
            style: color == null ? null : TextStyle(color: color.brightness.isDark ? Colors.white : Colors.black)),
        backgroundColor: color));
  }
}
0
likes
90
pub points
0%
popularity

Publisher

unverified uploader

Reverse engineered Google material tool allows to generate material palette from primary color

Repository (GitLab)
View/report issues

Documentation

API reference

License

BSD-3-Clause (LICENSE)

Dependencies

flutter

More

Packages that depend on flutterx_material_tool