neu 0.1.1 icon indicating copy to clipboard operation
neu: ^0.1.1 copied to clipboard

A simple helper for generating outstanding neumorphic-conforming designs.

example/lib/main.dart

import 'package:flutter/material.dart';

import 'package:neu/neu.dart';
import 'package:spectrum/spectrum.dart';
import 'package:url_launcher/url_launcher.dart';

// ignore_for_file: public_member_api_docs

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

/// [MaterialApp] frame.
class DemoFrame extends StatelessWidget {
  /// [MaterialApp] frame.
  const DemoFrame({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // final color = Colors.purpleAccent.shade200;
    // final color = Colors.purple.shade600;
    // final color = Colors.red.shade700;
    // final color = Colors.blue.shade600;
    // final color = Colors.green.shade600;
    final color = Colors.indigoAccent.shade700;
    // final color = Colors.orange.shade900;
    // final color = Colors.indigo.shade800;

    return AnimatedTheme(
      data: ThemeData.from(
        colorScheme:
            ColorScheme.fromSwatch(primarySwatch: color.asMaterialColor),
      ),
      duration: const Duration(milliseconds: 450),
      child: Builder(
        builder: (BuildContext _context) {
          return MaterialApp(
            debugShowCheckedModeBanner: false,
            color: color,
            theme: Theme.of(_context),
            home: Demo(color),
            // home:
            //  const Material(color: Colors.transparent, child: SliderDemo()),
          );
        },
      ),
    );
  }
}

/// Construct a [new Demo] `Widget` to fill a [DemoFrame].
class Demo extends StatelessWidget {
  /// Fill a [DemoFrame] with a [Scaffold] and [AppBar] whose body is a
  /// [PageView]. The `children` of this swiping page view are the customizable
  /// [SliderDemo], two variety of
  /// [buildBigView], and two instances of [buildSmallView]
  const Demo(this.color, {Key? key}) : super(key: key);

  /// This initial color from [DemoFrame] is provided along to [buildBigView]
  /// and [buildSmallView].
  final Color color;

  @override
  Widget build(BuildContext context) {
    final zabaFrog = InkWell(
      onTap: () => launch('https://pub.dev/publishers/zaba.app/packages'),
      child: const FittedBox(
        child: Padding(
          padding: EdgeInsets.all(20),
          child: Text('🐸', style: TextStyle(fontSize: 40)),
        ),
      ),
    );

    return Scaffold(
      // backgroundColor: Colors.black,
      backgroundColor: color,
      appBar: AppBar(
        title: const Text('neu'),
        actions: [zabaFrog],
      ),
      body: PageView(
        physics: const BouncingScrollPhysics(),
        children: [
          SliderDemo(color: color),
          const SliderDemo(),
          Demo0(color),
          Demo1(color),
          Demo2(color),
          Demo3(color),
        ],
      ),
    );
  }
}

/// Construct a [new SliderDemo] to create a page-filling `Widget` with a
/// malleable *[Neu]morphic* design container and several sliders to control
/// its properties. The `spread` property for large text is divided by 2.
class SliderDemo extends StatefulWidget {
  /// A page-filling `Widget` with a malleable *[Neu]morphic* design container
  /// and several sliders to control its properties. The `spread` property for
  /// large text is divided by 2.
  const SliderDemo({Key? key, this.color}) : super(key: key);

  /// If non-`null`, this provided [color] will disable this `Widget`'s "color
  /// slider" and set the visual appearance of the demo to this [color].
  final Color? color;

  @override
  _SliderDemoState createState() => _SliderDemoState();
}

class _SliderDemoState extends State<SliderDemo> {
  double colorValue = 0;
  Color get color => widget.color != null
      ? widget.color!
      : Colors.primaries[colorValue.round()];

  double depthValue = 25.0;
  int get depth => depthValue.round();

  double curvatureValue = 2;
  Curvature get curvature => Curvature.values[curvatureValue.round()];

  double swellValue = 2;
  Swell get swell => Swell.values[swellValue.round()];

  double spread = 7.5;

  static const alignments = [
    Alignment.topLeft,
    Alignment.topCenter,
    Alignment.topRight,
    Alignment.centerRight,
    Alignment.bottomRight,
    Alignment.bottomCenter,
    Alignment.bottomLeft,
    Alignment.centerLeft,
    Alignment.center,
  ];
  double lightSourceValue = 0;
  Alignment get lightSource => alignments[lightSourceValue.round()];

  @override
  Widget build(BuildContext context) {
    final neuDecoratedContainer = AnimatedContainer(
      decoration: Neu.boxDecoration(
        color: color,
        depth: depth,
        curvature: curvature,
        swell: swell,
        spread: spread,
        lightSource: lightSource,
        borderRadius: BorderRadius.circular(250.0),
      ),
      margin: const EdgeInsets.all(35.0),
      width: 300,
      height: 300,
      duration: const Duration(milliseconds: 1000),
      curve: Curves.elasticOut,
    );

    final style = Neu.textStyle(
      baseStyle: const TextStyle(fontSize: 50, fontWeight: FontWeight.w900),
      color: color,
      depth: depth,
      curvature: curvature,
      swell: swell,
      spread: spread / 2,
      lightSource: lightSource,
    );

    final colorSlider = Slider(
      activeColor: color.withWhite(75),
      inactiveColor: Colors.black45,
      label: '$color',
      value: colorValue,
      onChanged: (double slidTo) => setState(() =>
          colorValue = slidTo.clamp(0, Colors.primaries.length).toDouble()),
      max: Colors.primaries.length.toDouble() - 1,
      divisions: Colors.primaries.length,
    );
    final depthSlider = Slider(
      activeColor: color.withWhite(75),
      inactiveColor: Colors.black45,
      label: '$depth',
      value: depthValue,
      onChanged: (double slidTo) => setState(() => depthValue = slidTo),
      min: 1.0,
      max: 255,
      divisions: 255,
    );
    final spreadSlider = Slider(
      activeColor: color.withWhite(75),
      inactiveColor: Colors.black45,
      label: '$spread',
      value: spread,
      onChanged: (double slidTo) => setState(() => spread = slidTo),
      // onChanged: (double slidTo) {},
      // onChangeEnd: (double slidTo) => setState(() => spread = slidTo),
      min: 1.0,
      max: 50,
      divisions: 200,
    );
    final curvatureSlider = Slider(
      activeColor: color.withWhite(75),
      inactiveColor: Colors.black45,
      label: '$curvature',
      value: curvatureValue,
      onChanged: (double slidTo) => setState(() => curvatureValue = slidTo),
      max: 4,
      divisions: 4,
    );
    final swellSlider = Slider(
      activeColor: color.withWhite(75),
      inactiveColor: Colors.black45,
      label: '$swell',
      value: swellValue,
      onChanged: (double slidTo) => setState(() => swellValue = slidTo),
      max: 3,
      divisions: 3,
    );
    final lightSourceSlider = Slider(
      activeColor: color.withWhite(75),
      inactiveColor: Colors.black45,
      label: '$lightSource',
      value: lightSourceValue,
      onChanged: (double slidTo) => setState(() => lightSourceValue = slidTo),
      max: 8,
      divisions: 8,
    );

    SizedBox fixedWidth({double width = 200, required String label}) =>
        SizedBox(
            width: width, child: FittedBox(child: Text(label, style: style)));

    return
        //  AnimatedDefaultTextStyle(
        //   duration: const Duration(milliseconds: 600),
        //   curve: Curves.elasticOut,
        //   style: style,
        //   child:
        AnimatedContainer(
      color: color,
      duration: const Duration(milliseconds: 1000),
      curve: Curves.elasticOut,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        mainAxisSize: MainAxisSize.min,
        children: [
          Flexible(child: neuDecoratedContainer),
          const SizedBox(width: 50),
          Flexible(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                if (widget.color == null)
                  Row(children: [fixedWidth(label: 'COLOR:'), colorSlider]),
                Row(children: [fixedWidth(label: 'DEPTH:'), depthSlider]),
                Row(children: [fixedWidth(label: 'SPREAD:'), spreadSlider]),
                Row(children: [
                  fixedWidth(label: 'CURVATURE:'),
                  curvatureSlider
                ]),
                Row(children: [fixedWidth(label: 'SWELL:'), swellSlider]),
                Row(children: [
                  fixedWidth(label: 'LIGHT SOURCE:'),
                  lightSourceSlider,
                ]),
              ],
            ),
          ),
        ],
      ),
      // ),
    );
  }
}

class Demo0 extends StatelessWidget {
  const Demo0(this.color, {Key? key}) : super(key: key);
  final Color color;

  @override
  Widget build(BuildContext context) {
    return buildBigView(
      color: color,
      // size: 360.0,
      size: 300.0,
      depth: 20,
      spread: 18.0,
      // lightSource: const Alignment(-2, -2),
      // lightSource: const Alignment(-0.5, -0.5),
      lightSource: Alignment.topLeft,
      // lightSource: Alignment.topRight,
      // lightSource: Alignment.bottomRight,
    );
  }
}

class Demo1 extends StatelessWidget {
  const Demo1(this.color, {Key? key}) : super(key: key);
  final Color color;

  @override
  Widget build(BuildContext context) {
    return buildBigView(
      isToggles: true,
      color: color,
      // size: 360.0,
      size: 300.0,
      depth: 20,
      spread: 18.0,
      lightSource: Alignment.topLeft,
    );
  }
}

class Demo2 extends StatelessWidget {
  const Demo2(this.color, {Key? key}) : super(key: key);
  final Color color;

  @override
  Widget build(BuildContext context) {
    return buildSmallView(
      color: color,
      size: 120.0,
      depth: 8,
      spread: 7.5,
    );
  }
}

class Demo3 extends StatelessWidget {
  const Demo3(this.color, {Key? key}) : super(key: key);
  final Color color;

  @override
  Widget build(BuildContext context) {
    return buildSmallView(
      isToggles: true,
      color: color,
      size: 120.0,
      depth: 8,
      spread: 7.5,
    );
  }
}

class BuildNeuToggle extends StatefulWidget {
  const BuildNeuToggle(
    this.color, {
    Key? key,
    this.size = 120,
    this.text,
    required this.depth,
    required this.spread,
    this.lightSource = Alignment.topLeft,
    required this.textDepth,
  }) : super(key: key);

  final Color color;
  final String? text;
  final double size, spread;
  final int depth, textDepth;
  final Alignment lightSource;

  @override
  _BuildNeuToggleState createState() => _BuildNeuToggleState();
}

class _BuildNeuToggleState extends State<BuildNeuToggle> {
  var isPressed = false;
  @override
  Widget build(BuildContext context) {
    return NeuToggle(
      color: widget.color,
      depth: widget.depth,
      spread: widget.spread,
      // isFlat: true,
      // isSuper: true,

      onToggle: (bool isToggled) => setState(() => isPressed = isToggled),
      providesFeedback: true,
      duration: const Duration(milliseconds: 1200),
      curve: Curves.elasticOut,

      insets: EdgeInsets.zero,
      margin: EdgeInsets.only(
        left: 25 * (widget.size > 150 ? 3 : 1),
        top: 25 * (widget.size > 150 ? 3 : 1),
        right: 25 * (widget.size > 150 ? 3 : 1),
        bottom: 0,
      ),
      width: widget.size,
      height: widget.size,

      lightSource: widget.lightSource,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(5.0),
        // side: BorderSide(width: 0.5, color: widget.color.withWhite(10)),
        side: BorderSide(
          width: isPressed
              ? 0.5
              : widget.size > 200
                  ? 0.5
                  : 1.0,
          color:
              isPressed ? widget.color.withBlack(widget.depth) : widget.color,
        ),
      ),

      /// Child
      child: Padding(
        padding: const EdgeInsets.all(7.5),
        child: FittedBox(
          child: Text(
            widget.text ?? ' ${isPressed.toString()}   ',
            textAlign: TextAlign.center,
            style: Neu.textStyle(
              // color: widget.color ~/ Colors.black,
              color: widget.color,
              spread: isPressed ? 3 : 8,
              depth: isPressed
                  ? (widget.textDepth * 1.5).round()
                  : widget.textDepth,
              swell: isPressed ? Swell.superdeboss : Swell.emboss,
              baseStyle: const TextStyle(
                fontSize: 100,
                fontWeight: FontWeight.w900,
                letterSpacing: 1.0,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Widget buildNeu(
  Color color, {
  double size = 120,
  String text = '',
  required int depth,
  required double spread,
  required Curvature curvature,
  required Swell swell,
  Alignment lightSource = Alignment.topLeft,
  required int textDepth,
}) {
  final neuObject = Neu(
    // final neuObject = Neu.toggle(
    color: color,
    depth: depth,
    spread: spread,

    /// These parameters (curvature and swell) for `Neu.toggle` are handled
    /// dynamically by the below flags.
    curvature: curvature,
    swell: swell,

    /// These flags are only valid for `Neu.toggle` where they drive the
    /// curvature and swell based on true or false states.
    // isPressed: isPressed,
    // isFlat: true,
    // isSuper: true,

    lightSource: lightSource,
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(3.0),
      // side: BorderSide(width: 0.5, color: color.withWhite(10)),
      // side: BorderSide(width: 0.5, color: color),
    ),
  );

  return AnimatedContainer(
    duration: const Duration(milliseconds: 450),
    margin: EdgeInsets.only(
      left: 25 * (size > 150 ? 3 : 1),
      top: 25 * (size > 150 ? 3 : 1),
      right: 25 * (size > 150 ? 3 : 1),
      bottom: 0,
    ),
    width: size,
    height: size,
    decoration: neuObject.buildShapeDecoration,
    child: Padding(
      padding: const EdgeInsets.all(15),
      child: FittedBox(
        child: Text(
          text,
          textAlign: TextAlign.center,
          style: Neu.textStyle(
            // color: color ~/ Colors.black,
            color: color,
            spread: textDepth < 30 ? 5 : 4,
            depth: textDepth,
            // swell: Swell.superemboss,
            swell: swell,
            baseStyle: const TextStyle(
              fontSize: 40, // fontSize impacts the [Shadow]s underneath
              fontWeight: FontWeight.w900,
              letterSpacing: 1.0,
            ),
          ),
        ),
      ),
    ),
  );
}

/// One entire page for a [PageView]. Comprised of a [SingleChildScrollView]
/// and a [Column].
Widget buildBigView({
  bool isToggles = false,
  required Color color,
  required double size,
  required int depth,
  required double spread,
  required Alignment lightSource,
  String text = 'Text',
  int textDepth = 25,
}) {
  const texts = [
    'Swell.superemboss\nCurvature.superconcave',
    'Swell.superemboss\nCurvature.concave',
    'Swell.superemboss\nCurvature.flat',
    'Swell.superemboss\nCurvature.convex',
    'Swell.superemboss\nCurvature.superconvex',
    'Swell.emboss\nCurvature.superconcave',
    'Swell.emboss\nCurvature.concave',
    'Swell.emboss\nCurvature.flat',
    'Swell.emboss\nCurvature.convex',
    'Swell.emboss\nCurvature.superconvex',
    'Swell.deboss\nCurvature.superconcave',
    'Swell.deboss\nCurvature.concave',
    'Swell.deboss\nCurvature.flat',
    'Swell.deboss\nCurvature.convex',
    'Swell.deboss\nCurvature.superconvex',
    'Swell.superdeboss\nCurvature.superconcave',
    'Swell.superdeboss\nCurvature.concave',
    'Swell.superdeboss\nCurvature.flat',
    'Swell.superdeboss\nCurvature.convex',
    'Swell.superdeboss\nCurvature.superconvex',
  ];
  const curvatures = [
    Curvature.superconcave,
    Curvature.concave,
    Curvature.flat,
    Curvature.convex,
    Curvature.superconvex,
    Curvature.superconcave,
    Curvature.concave,
    Curvature.flat,
    Curvature.convex,
    Curvature.superconvex,
    Curvature.superconcave,
    Curvature.concave,
    Curvature.flat,
    Curvature.convex,
    Curvature.superconvex,
    Curvature.superconcave,
    Curvature.concave,
    Curvature.flat,
    Curvature.convex,
    Curvature.superconvex,
  ];
  const swells = [
    Swell.superemboss,
    Swell.superemboss,
    Swell.superemboss,
    Swell.superemboss,
    Swell.superemboss,
    Swell.emboss,
    Swell.emboss,
    Swell.emboss,
    Swell.emboss,
    Swell.emboss,
    Swell.deboss,
    Swell.deboss,
    Swell.deboss,
    Swell.deboss,
    Swell.deboss,
    Swell.superdeboss,
    Swell.superdeboss,
    Swell.superdeboss,
    Swell.superdeboss,
    Swell.superdeboss,
  ];

  return SingleChildScrollView(
    child: Wrap(
      alignment: WrapAlignment.center,
      children: [
        for (var count = 0; count < texts.length; count++)
          isToggles
              ? BuildNeuToggle(
                  color,
                  size: size,
                  lightSource: lightSource,
                  // text: texts[count],
                  depth: depth,
                  spread: spread,
                  textDepth: textDepth,
                )
              : buildNeu(
                  color,
                  size: size,
                  lightSource: lightSource,
                  text: texts[count],
                  depth: depth,
                  spread: spread,
                  curvature: curvatures[count],
                  swell: swells[count],
                  textDepth: textDepth,
                ),

        ///
        const SizedBox(width: 1000, height: 75),
      ],
    ),
  );
}

/// One entire page for a [PageView]. Comprised of a [SingleChildScrollView]
/// and a [Column].
Widget buildSmallView({
  bool isToggles = false,
  required Color color,
  required double size,
  required int depth,
  required double spread,
  String text = '',
  int textDepth = 40,
}) {
  const sizedBox = SizedBox(width: 1000, height: 50);

  const texts = [
    'Swell.\nsuperemboss\n\nCurvature.\nsuperconcave',
    'Swell.\nsuperemboss\n\nCurvature.\nconcave',
    'Swell.\nsuperemboss\n\nCurvature.\nflat',
    'Swell.\nsuperemboss\n\nCurvature.\nflat',
    'Swell.\nsuperemboss\n\nCurvature.\nconvex',
    'Swell.\nsuperemboss\n\nCurvature.\nsuperconvex',
    'Swell.\nemboss\n\nCurvature.\nsuperconcave',
    'Swell.\nemboss\n\nCurvature.\nconcave',
    'Swell.\nemboss\n\nCurvature.\nflat',
    'Swell.\nemboss\n\nCurvature.\nflat',
    'Swell.\nemboss\n\nCurvature.\nconvex',
    'Swell.\nemboss\n\nCurvature.\nsuperconvex',
    'Swell.\ndeboss\n\nCurvature.\nsuperconcave',
    'Swell.\ndeboss\n\nCurvature.\nconcave',
    'Swell.\ndeboss\n\nCurvature.\nflat',
    'Swell.\ndeboss\n\nCurvature.\nflat',
    'Swell.\ndeboss\n\nCurvature.\nconvex',
    'Swell.\ndeboss\n\nCurvature.\nsuperconvex',
    'Swell.\nsuperdeboss\n\nCurvature.\nsuperconcave',
    'Swell.\nsuperdeboss\n\nCurvature.\nconcave',
    'Swell.\nsuperdeboss\n\nCurvature.\nflat',
    'Swell.\nsuperdeboss\n\nCurvature.\nflat',
    'Swell.\nsuperdeboss\n\nCurvature.\nconvex',
    'Swell.\nsuperdeboss\n\nCurvature.\nsuperconvex',
  ];
  const curvatures = [
    Curvature.superconcave,
    Curvature.concave,
    Curvature.flat,
    Curvature.flat,
    Curvature.convex,
    Curvature.superconvex,
    Curvature.superconcave,
    Curvature.concave,
    Curvature.flat,
    Curvature.flat,
    Curvature.convex,
    Curvature.superconvex,
    Curvature.superconcave,
    Curvature.concave,
    Curvature.flat,
    Curvature.flat,
    Curvature.convex,
    Curvature.superconvex,
    Curvature.superconcave,
    Curvature.concave,
    Curvature.flat,
    Curvature.flat,
    Curvature.convex,
    Curvature.superconvex,
  ];
  const swells = [
    Swell.superemboss,
    Swell.superemboss,
    Swell.superemboss,
    Swell.superemboss,
    Swell.superemboss,
    Swell.superemboss,
    Swell.emboss,
    Swell.emboss,
    Swell.emboss,
    Swell.emboss,
    Swell.emboss,
    Swell.emboss,
    Swell.deboss,
    Swell.deboss,
    Swell.deboss,
    Swell.deboss,
    Swell.deboss,
    Swell.deboss,
    Swell.superdeboss,
    Swell.superdeboss,
    Swell.superdeboss,
    Swell.superdeboss,
    Swell.superdeboss,
    Swell.superdeboss,
  ];

  return SingleChildScrollView(
    child: Wrap(
      alignment: WrapAlignment.center,
      children: [
        for (var count = 0; count < texts.length; count++)
          isToggles
              ? BuildNeuToggle(
                  color,
                  size: size,
                  // text: texts[count],
                  depth: depth,
                  spread: spread,
                  textDepth: textDepth,
                )
              : buildNeu(
                  color,
                  size: size,
                  text: texts[count],
                  depth: depth,
                  spread: spread,
                  curvature: curvatures[count],
                  swell: swells[count],
                  textDepth: textDepth,
                ),
      ]
        // Spacers
        ..insert(0, sizedBox)
        ..insert(7, sizedBox)
        ..insert(14, sizedBox)
        ..insert(21, sizedBox)
        ..insert(28, sizedBox),
    ),
  );
}
8
likes
120
pub points
43%
popularity

Publisher

verified publisher iconzaba.app

A simple helper for generating outstanding neumorphic-conforming designs.

Repository (GitHub)
View/report issues

Documentation

API reference

License

BSD-3-Clause (LICENSE)

Dependencies

flutter, spectrum

More

Packages that depend on neu