flutter_util_widgets.dart - We're Null Safety ;)

pub package License: MIT Build Status

A flutter package that provides a variety of useful widgets. It's constantly updated.

Install the package

1. Add this to your package's pubspec.yaml file:

flutter_util_widgets: ^1.1.0+1

2. Update dependencies:

You can install packages from the command line:

$ flutter pub get

3. Import it on your app

Now in your Dart code, you can use:

import 'package:flutter_util_widgets/flutter_util_widgets.dart';

Using the package

1. Switcher widget:

Screenshot:
Code:
import 'package:flutter/material.dart';

import 'package:flutter_util_widgets/flutter_util_widgets.dart';

class SwitcherExample extends StatefulWidget {
  @override
  _SwitcherExampleState createState() => _SwitcherExampleState();
}

class _SwitcherExampleState extends State<SwitcherExample> {
  bool squared = false;
  bool rounded = false;
  bool withLabel = false;
  bool withLabelRounded = false;
  bool customActiveColor = false;
  bool customDisabledColorAndAlignment = false;

  void setNewValue(Function(bool, bool) apply, bool value, bool newValue) {
    setState(() {
      apply(value, newValue);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Switcher Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Row(
              children: <Widget>[
                Expanded(
                  child: Text(
                    'Squared',
                  ),
                ),
                Switcher(
                  value: squared,
                  onChange: (value) {
                    setNewValue((let, val) => squared = value, squared, value);
                  },
                ),
              ],
            ),
            Row(
              children: <Widget>[
                Expanded(
                  child: Text(
                    'Rounded',
                  ),
                ),
                Switcher.rounded(
                  value: rounded,
                  onChange: (value) {
                    setNewValue((let, val) => rounded = value, rounded, value);
                  },
                ),
              ],
            ),
            Switcher.label(
              label: 'With label',
              value: withLabel,
              onChange: (value) {
                setNewValue((let, val) => withLabel = value, withLabel, value);
              },
            ),
            Switcher.labelAndRounded(
              label: 'With label and rounded',
              value: withLabelRounded,
              onChange: (value) {
                setNewValue((let, val) => withLabelRounded = value, withLabelRounded,
                    value);
              },
            ),
            Switcher.label(
              value: customActiveColor,
              label: 'Customizing active color and label style',
              labelStyle: TextStyle(color: Theme.of(context).primaryColor),
              activeColor: Colors.amber,
              onChange: (value) {
                setNewValue((let, val) => customActiveColor = value,
                    customActiveColor, value);
              },
            ),
            Switcher.labelAndRounded(
              mainAxisAlignment: MainAxisAlignment.end,
              label: 'Customizing disabled color and alignment',
              disableColor: Colors.blueGrey[300],
              value: customDisabledColorAndAlignment,
              onChange: (value) {
                setNewValue((let, val) => customDisabledColorAndAlignment = value,
                    customDisabledColorAndAlignment, value);
              },
            ),
            const Divider(),
            const Text(
              'Current values:',
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
            Text('Squared: $squared'),
            Text('Rounded: $rounded'),
            Text('With label: $withLabel'),
            Text('With label and rounded: $withLabelRounded'),
            Text('Customizing active color and label style: $customActiveColor'),
            Text('Customizing disabled color and alignment: $customDisabledColorAndAlignment'),
          ],
        ),
      ),
    );
  }
}

2. TypeSelector widget:

OneTypeSelector is TypeSelector now, with support for multiple choices (list of values)!!
Screenshot:
Code:
Class Person:
class Person {
  final String name;
  DateTime? birthMonth;
  bool? isSingle;
  List<String> favoriteAnimes = [];
  String? favoriteCharacter;

  Person(this.name);

  @override
  String toString() {
    return 'Name: $name - Birth month: ${birthMonth?.month} - Is single: $isSingle - Favorite animes: $favoriteAnimes - Favorite character: $favoriteCharacter';
  }
}
import 'package:flutter/material.dart';

import 'package:flutter_util_widgets/flutter_util_widgets.dart';

const TextStyle _bold = TextStyle(fontWeight: FontWeight.bold);

class TypeSelectorExample extends StatefulWidget {
  @override
  _TypeSelectorExampleState createState() => _TypeSelectorExampleState();
}

class _TypeSelectorExampleState extends State<TypeSelectorExample> {
  Person otaku = Person('Otaku Sr.');
  Person p1 = Person('Person 1');
  Person p2 = Person('Person 2');
  Person? selected;

  List<String> characters = [
    'Eren Yeager',
    'Kurozaki Ichigo',
    'Uzumaki Naruto',
    'Son Goku',
    'Kamado Tanjiro',
  ];

  List<String> animes = [
    'Attack on Titan',
    'Bleach',
    'Naruto',
    'Dragon Ball Z',
    'Demon Slayer',
  ];

  List<DateTime> months = [
    month(DateTime.january),
    month(DateTime.february),
    month(DateTime.march),
    month(DateTime.april),
    month(DateTime.may),
    month(DateTime.june),
    month(DateTime.july),
    month(DateTime.august),
    month(DateTime.september),
    month(DateTime.october),
    month(DateTime.november),
    month(DateTime.december),
  ];

  /// Aux method to get <current-year> - MONTH - <first-day-of-month>
  static DateTime month(int month) => DateTime(DateTime.now().year, month, 1);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('One Type Selector Example'),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                const Text('Favorite character?', style: _bold),
                const SizedBox(height: 4.0),
                TypeSelector<String>(
                  value: otaku.favoriteCharacter,
                  options: characters,
                  onValueChanged: (String value) {
                    setState(() {
                      this.otaku.favoriteCharacter = value;
                    });
                  },
                ),
              ],
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                const Text('Top 3 Favorite animes? (multiple values)',
                    style: bolded),
                const SizedBox(height: 4.0),
                TypeSelector<String>(
                  multiple: true,
                  maxQuantityValues: 3,
                  onMaxQuantityValues: (max) {
                    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                      content: Text('Max quantity reached: $max'),
                      backgroundColor: Colors.red,
                    ));
                  },
                  values: otaku.favoriteAnimes,
                  options: animes,
                  optionWidth: 150.0,
                  toggleOptionOnTap: true,
                  separatorWidth: 0.0,
                  contentPadding: const EdgeInsets.all(4.0),
                  onValuesChanged: (List<String> values) {
                    setState(() {
                      this.otaku.favoriteAnimes = values;
                    });
                  },
                ),
              ],
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                const Text('Birth month?', style: _bold),
                const SizedBox(height: 4.0),
                TypeSelector<DateTime>.rect(
                  value: otaku.birthMonth,
                  options: months,
                  activeColor: Colors.amber,
                  onValueChanged: (DateTime value) {
                    setState(() {
                      this.otaku.birthMonth = value;
                    });
                  },
                  setValueLabel: (DateTime value) {
                    return value.month.toString().padLeft(2, '0');
                  },
                ),
              ],
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                const Text('Is single?', style: _bold),
                const SizedBox(height: 4.0),
                TypeSelector<bool>(
                  value: otaku.isSingle,
                  options: [true, false],
                  onValueChanged: (bool value) {
                    setState(() {
                      this.otaku.isSingle = value;
                    });
                  },
                ),
              ],
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                const Text('Select a person?', style: bolded),
                const SizedBox(height: 4.0),
                TypeSelector<Person>(
                  value: selected,
                  options: [p1, p2],
                  setValueLabel: (person) => person.name,
                  comparingBy: (person) => person.name,
                  onValueChanged: (Person value) {
                    setState(() {
                      this.selected = value;
                    });
                  },
                ),
              ],
            ),
          ),
          const Divider(),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Text(
                  'Who am I?',
                  style: bolded.copyWith(color: Theme.of(context).accentColor),
                ),
                viewValues('Name: ', otaku.name),
                viewValues('Favorite character: ', otaku.favoriteCharacter),
                viewValues(
                    'Top 3 Favorite animes: ', otaku.favoriteAnimes.join(', ')),
                viewValues('Birth month: ', otaku.birthMonth?.month),
                viewValues('Is single: ', otaku.isSingle),
                viewValues('Selected person: ', selected?.name),
              ],
            ),
          ),
        ],
      ),
    );
  }
}