Skip to content

Instantly share code, notes, and snippets.

@exavolt
Last active November 8, 2024 15:24
Show Gist options
  • Select an option

  • Save exavolt/8b303b9d556f1cf74f2282917b79a631 to your computer and use it in GitHub Desktop.

Select an option

Save exavolt/8b303b9d556f1cf74f2282917b79a631 to your computer and use it in GitHub Desktop.

Revisions

  1. exavolt revised this gist Jul 17, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    # Choice Chip Bar for Flutter

    While Flutter already provide `ChoiceChip`, it's a bare-bone component. We would need to wire them ourself to create a group of choices. This widget, `ChoiceChipBar`, means to provide a self-contained widget for a group of choice chips, so the anchestor widget won't need to manage the state; just provide the choices and provide callback to `onChanged`.
    While Flutter already provides `ChoiceChip`, it's a bare-bone component. We would need to wire them ourselves to create a group of choices. This widget, `ChoiceChipBar`, means to provide a self-contained widget for a group of choice chips, so the ancestor widget won't need to manage the state; just provide the choices and a callback to `onChanged`.

    ![Screenshot_1594966144 copy](https://user-images.githubusercontent.com/77626/87754377-f2749980-c82e-11ea-86fd-a72ffbe75b1e.png)
  2. exavolt revised this gist Jul 17, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion example.dart
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    //

    import 'package:flutter/material.dart';
    import 'package:widget_sandbox/toys/choice_chip_bar/choice_chip_bar.dart';
    import 'choice_chip_bar.dart';

    class ChoiceChipBarPage extends StatefulWidget {
    static Route<void> createRoute(RouteSettings settings) => MaterialPageRoute(
  3. exavolt revised this gist Jul 17, 2020. 1 changed file with 63 additions and 99 deletions.
    162 changes: 63 additions & 99 deletions example.dart
    Original file line number Diff line number Diff line change
    @@ -1,113 +1,77 @@
    //

    import 'package:flutter/material.dart';
    import 'package:widget_sandbox/toys/choice_chip_bar/choice_chip_bar.dart';

    // https://material.io/components/chips#choice-chips
    class ChoiceChipBar<T> extends StatefulWidget {
    ChoiceChipBar({
    Key key,
    @required this.choices,
    this.initialValue,
    this.allowDeselect,
    this.onChanged,
    this.choiceText,
    this.choiceLabel,
    this.choiceAvatar,
    this.padding,
    this.color,
    this.shape,
    this.labelStyle,
    this.selectedColor,
    this.selectedShape,
    this.selectedLabelStyle,
    }) : assert(choices.isNotEmpty == true),
    super(key: key);

    final List<T> choices;
    final T initialValue;
    final bool allowDeselect;
    final ValueChanged<T> onChanged;

    // A callback to generate label text for each choice. This will be ignored
    // if [choiceLabel] is not null.
    final String Function(T e) choiceText;

    // Widget builder used to create the label for each choice. This overrides
    // [choiceText]. The returned widget must not be null.
    final Widget Function(BuildContext context, T e, bool selected) choiceLabel;

    // Widget builder used to create the avatar for each choice.
    final Widget Function(BuildContext context, T e, bool selected) choiceAvatar;

    final EdgeInsets padding;
    final Color color;
    final ShapeBorder shape;
    final TextStyle labelStyle;
    final Color selectedColor;
    final ShapeBorder selectedShape;
    final TextStyle selectedLabelStyle;
    class ChoiceChipBarPage extends StatefulWidget {
    static Route<void> createRoute(RouteSettings settings) => MaterialPageRoute(
    builder: (BuildContext context) => ChoiceChipBarPage(),
    settings: settings,
    );

    @override
    _ChoiceChipBarState<T> createState() => _ChoiceChipBarState<T>();
    _ChoiceChipBarPageState createState() => _ChoiceChipBarPageState();
    }

    class _ChoiceChipBarState<T> extends State<ChoiceChipBar<T>> {
    T _value;

    @override
    void initState() {
    super.initState();

    _value = widget.initialValue ?? widget.choices.first;
    }

    @override
    void dispose() {
    super.dispose();
    }
    class _ChoiceChipBarPageState extends State<ChoiceChipBarPage> {
    Hardness _selectedHardness = Hardness.medium;

    @override
    Widget build(BuildContext context) {
    final contentWidget = Wrap(
    spacing: 8,
    children: widget.choices.map<Widget>((e) {
    final selected = _value == e;
    return ChoiceChip(
    backgroundColor: widget.color,
    selectedColor: widget.selectedColor,
    shape: selected ? widget.selectedShape : widget.shape,
    avatar: widget.choiceAvatar?.call(context, e, selected),
    label: widget.choiceLabel == null
    ? Text(
    widget.choiceText == null ? '$e' : widget.choiceText(e),
    style: selected
    ? widget.selectedLabelStyle
    : widget.labelStyle,
    )
    : widget.choiceLabel(context, e, selected),
    selected: selected,
    onSelected: (bool on) {
    if (!on && !widget.allowDeselect) {
    return;
    }
    // Can we, rather than calling setState, rebuild only the
    // the widgets which we move the selection from and to?
    setState(() {
    _value = on ? e : null;
    widget.onChanged?.call(_value);
    });
    },
    );
    }).toList(growable: false));
    final paddedContent = widget.padding != null
    ? Padding(
    padding: widget.padding,
    child: contentWidget,
    )
    : contentWidget;
    return SingleChildScrollView(
    scrollDirection: Axis.horizontal,
    child: paddedContent,
    final theme = Theme.of(context);
    return Scaffold(
    appBar: AppBar(
    title: Text('Choice Chip Bar'),
    ),
    body: Padding(
    padding: const EdgeInsets.symmetric(vertical: 16),
    child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: <Widget>[
    Padding(
    padding: const EdgeInsets.symmetric(horizontal: 16),
    child: Text(
    'Select type',
    style: theme.textTheme.subtitle1,
    ),
    ),
    ChoiceChipBar<Hardness>(
    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
    choices: Hardness.values,
    initialValue: _selectedHardness,
    onChanged: (Hardness selected) {
    setState(() {
    _selectedHardness = selected;
    });
    },
    choiceText: (Hardness c) {
    switch (c) {
    case Hardness.extraSoft:
    return 'Extra Soft';
    case Hardness.soft:
    return 'Soft';
    case Hardness.medium:
    return 'Medium';
    case Hardness.hard:
    return 'Hard';
    case Hardness.extraHard:
    return 'Extra Hard';
    default:
    return '<undefined>';
    }
    },
    ),
    ],
    ),
    ),
    );
    }
    }

    enum Hardness {
    extraSoft,
    soft,
    medium,
    hard,
    extraHard,
    }
  4. exavolt revised this gist Jul 17, 2020. 3 changed files with 129 additions and 71 deletions.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    # Choice Chip Bar for Flutter

    While Flutter already provide `ChoiceChip`, it's just a bare-bone component. We would need to wire them ourself to create a group of choices. This widget, `ChoiceChipBar`, means to provide a self-contained group of choice chips, so the anchestor widget won't need to manage the state.
    While Flutter already provide `ChoiceChip`, it's a bare-bone component. We would need to wire them ourself to create a group of choices. This widget, `ChoiceChipBar`, means to provide a self-contained widget for a group of choice chips, so the anchestor widget won't need to manage the state; just provide the choices and provide callback to `onChanged`.

    ![Screenshot_1594966144 copy](https://user-images.githubusercontent.com/77626/87754377-f2749980-c82e-11ea-86fd-a72ffbe75b1e.png)
    36 changes: 29 additions & 7 deletions choice_chip_bar.dart
    Original file line number Diff line number Diff line change
    @@ -8,8 +8,11 @@ class ChoiceChipBar<T> extends StatefulWidget {
    Key key,
    @required this.choices,
    this.initialValue,
    this.allowDeselect,
    this.onChanged,
    this.choiceText,
    this.choiceLabel,
    this.choiceAvatar,
    this.padding,
    this.color,
    this.shape,
    @@ -22,9 +25,20 @@ class ChoiceChipBar<T> extends StatefulWidget {

    final List<T> choices;
    final T initialValue;
    final bool allowDeselect;
    final ValueChanged<T> onChanged;

    // A callback to generate label text for each choice. This will be ignored
    // if [choiceLabel] is not null.
    final String Function(T e) choiceText;

    // Widget builder used to create the label for each choice. This overrides
    // [choiceText]. The returned widget must not be null.
    final Widget Function(BuildContext context, T e, bool selected) choiceLabel;

    // Widget builder used to create the avatar for each choice.
    final Widget Function(BuildContext context, T e, bool selected) choiceAvatar;

    final EdgeInsets padding;
    final Color color;
    final ShapeBorder shape;
    @@ -62,17 +76,25 @@ class _ChoiceChipBarState<T> extends State<ChoiceChipBar<T>> {
    backgroundColor: widget.color,
    selectedColor: widget.selectedColor,
    shape: selected ? widget.selectedShape : widget.shape,
    label: Text(
    widget.choiceText == null ? '$e' : widget.choiceText(e),
    style: selected ? widget.selectedLabelStyle : widget.labelStyle,
    ),
    avatar: widget.choiceAvatar?.call(context, e, selected),
    label: widget.choiceLabel == null
    ? Text(
    widget.choiceText == null ? '$e' : widget.choiceText(e),
    style: selected
    ? widget.selectedLabelStyle
    : widget.labelStyle,
    )
    : widget.choiceLabel(context, e, selected),
    selected: selected,
    onSelected: (bool on) {
    if (!on && !widget.allowDeselect) {
    return;
    }
    // Can we, rather than calling setState, rebuild only the
    // the widgets which we move the selection from and to?
    setState(() {
    _value = on ? e : null;
    if (on) {
    widget.onChanged?.call(e);
    }
    widget.onChanged?.call(_value);
    });
    },
    );
    162 changes: 99 additions & 63 deletions example.dart
    Original file line number Diff line number Diff line change
    @@ -1,77 +1,113 @@
    //

    import 'package:flutter/material.dart';
    import 'choice_chip_bar.dart';

    class ChoiceChipBarPage extends StatefulWidget {
    static Route<void> createRoute(RouteSettings settings) => MaterialPageRoute(
    builder: (BuildContext context) => ChoiceChipBarPage(),
    settings: settings,
    );
    // https://material.io/components/chips#choice-chips
    class ChoiceChipBar<T> extends StatefulWidget {
    ChoiceChipBar({
    Key key,
    @required this.choices,
    this.initialValue,
    this.allowDeselect,
    this.onChanged,
    this.choiceText,
    this.choiceLabel,
    this.choiceAvatar,
    this.padding,
    this.color,
    this.shape,
    this.labelStyle,
    this.selectedColor,
    this.selectedShape,
    this.selectedLabelStyle,
    }) : assert(choices.isNotEmpty == true),
    super(key: key);

    final List<T> choices;
    final T initialValue;
    final bool allowDeselect;
    final ValueChanged<T> onChanged;

    // A callback to generate label text for each choice. This will be ignored
    // if [choiceLabel] is not null.
    final String Function(T e) choiceText;

    // Widget builder used to create the label for each choice. This overrides
    // [choiceText]. The returned widget must not be null.
    final Widget Function(BuildContext context, T e, bool selected) choiceLabel;

    // Widget builder used to create the avatar for each choice.
    final Widget Function(BuildContext context, T e, bool selected) choiceAvatar;

    final EdgeInsets padding;
    final Color color;
    final ShapeBorder shape;
    final TextStyle labelStyle;
    final Color selectedColor;
    final ShapeBorder selectedShape;
    final TextStyle selectedLabelStyle;

    @override
    _ChoiceChipBarPageState createState() => _ChoiceChipBarPageState();
    _ChoiceChipBarState<T> createState() => _ChoiceChipBarState<T>();
    }

    class _ChoiceChipBarPageState extends State<ChoiceChipBarPage> {
    Hardness _selectedHardness = Hardness.medium;
    class _ChoiceChipBarState<T> extends State<ChoiceChipBar<T>> {
    T _value;

    @override
    void initState() {
    super.initState();

    _value = widget.initialValue ?? widget.choices.first;
    }

    @override
    void dispose() {
    super.dispose();
    }

    @override
    Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Scaffold(
    appBar: AppBar(
    title: Text('Choice Chip Bar'),
    ),
    body: Padding(
    padding: const EdgeInsets.symmetric(vertical: 16),
    child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: <Widget>[
    Padding(
    padding: const EdgeInsets.symmetric(horizontal: 16),
    child: Text(
    'Select type',
    style: theme.textTheme.subtitle1,
    ),
    ),
    ChoiceChipBar<Hardness>(
    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
    choices: Hardness.values,
    initialValue: _selectedHardness,
    onChanged: (Hardness selected) {
    setState(() {
    _selectedHardness = selected;
    });
    },
    choiceText: (Hardness c) {
    switch (c) {
    case Hardness.extraSoft:
    return 'Extra Soft';
    case Hardness.soft:
    return 'Soft';
    case Hardness.medium:
    return 'Medium';
    case Hardness.hard:
    return 'Hard';
    case Hardness.extraHard:
    return 'Extra Hard';
    default:
    return '<undefined>';
    }
    },
    ),
    ],
    ),
    ),
    final contentWidget = Wrap(
    spacing: 8,
    children: widget.choices.map<Widget>((e) {
    final selected = _value == e;
    return ChoiceChip(
    backgroundColor: widget.color,
    selectedColor: widget.selectedColor,
    shape: selected ? widget.selectedShape : widget.shape,
    avatar: widget.choiceAvatar?.call(context, e, selected),
    label: widget.choiceLabel == null
    ? Text(
    widget.choiceText == null ? '$e' : widget.choiceText(e),
    style: selected
    ? widget.selectedLabelStyle
    : widget.labelStyle,
    )
    : widget.choiceLabel(context, e, selected),
    selected: selected,
    onSelected: (bool on) {
    if (!on && !widget.allowDeselect) {
    return;
    }
    // Can we, rather than calling setState, rebuild only the
    // the widgets which we move the selection from and to?
    setState(() {
    _value = on ? e : null;
    widget.onChanged?.call(_value);
    });
    },
    );
    }).toList(growable: false));
    final paddedContent = widget.padding != null
    ? Padding(
    padding: widget.padding,
    child: contentWidget,
    )
    : contentWidget;
    return SingleChildScrollView(
    scrollDirection: Axis.horizontal,
    child: paddedContent,
    );
    }
    }

    enum Hardness {
    extraSoft,
    soft,
    medium,
    hard,
    extraHard,
    }
  5. exavolt revised this gist Jul 17, 2020. No changes.
  6. exavolt revised this gist Jul 17, 2020. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    # Choice Chip Bar for Flutter

    While Flutter already provide `ChoiceChip`, it's just a bare-bone component. We would need to wire them ourself to create a group of choices. This widget, `ChoiceChipBar`, means to provide a self-contained group of choice chips, so the anchestor widget won't need to manage the state.

    ![Screenshot_1594966144 copy](https://user-images.githubusercontent.com/77626/87754377-f2749980-c82e-11ea-86fd-a72ffbe75b1e.png)
  7. exavolt created this gist Jul 17, 2020.
    3 changes: 3 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    # Choice Chip Bar for Flutter

    While Flutter already provide `ChoiceChip`, it's just a bare-bone component. We would need to wire them ourself to create a group of choices. This widget, `ChoiceChipBar`, means to provide a self-contained group of choice chips, so the anchestor widget won't need to manage the state.
    91 changes: 91 additions & 0 deletions choice_chip_bar.dart
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,91 @@
    //

    import 'package:flutter/material.dart';

    // https://material.io/components/chips#choice-chips
    class ChoiceChipBar<T> extends StatefulWidget {
    ChoiceChipBar({
    Key key,
    @required this.choices,
    this.initialValue,
    this.onChanged,
    this.choiceText,
    this.padding,
    this.color,
    this.shape,
    this.labelStyle,
    this.selectedColor,
    this.selectedShape,
    this.selectedLabelStyle,
    }) : assert(choices.isNotEmpty == true),
    super(key: key);

    final List<T> choices;
    final T initialValue;
    final ValueChanged<T> onChanged;
    final String Function(T e) choiceText;

    final EdgeInsets padding;
    final Color color;
    final ShapeBorder shape;
    final TextStyle labelStyle;
    final Color selectedColor;
    final ShapeBorder selectedShape;
    final TextStyle selectedLabelStyle;

    @override
    _ChoiceChipBarState<T> createState() => _ChoiceChipBarState<T>();
    }

    class _ChoiceChipBarState<T> extends State<ChoiceChipBar<T>> {
    T _value;

    @override
    void initState() {
    super.initState();

    _value = widget.initialValue ?? widget.choices.first;
    }

    @override
    void dispose() {
    super.dispose();
    }

    @override
    Widget build(BuildContext context) {
    final contentWidget = Wrap(
    spacing: 8,
    children: widget.choices.map<Widget>((e) {
    final selected = _value == e;
    return ChoiceChip(
    backgroundColor: widget.color,
    selectedColor: widget.selectedColor,
    shape: selected ? widget.selectedShape : widget.shape,
    label: Text(
    widget.choiceText == null ? '$e' : widget.choiceText(e),
    style: selected ? widget.selectedLabelStyle : widget.labelStyle,
    ),
    selected: selected,
    onSelected: (bool on) {
    setState(() {
    _value = on ? e : null;
    if (on) {
    widget.onChanged?.call(e);
    }
    });
    },
    );
    }).toList(growable: false));
    final paddedContent = widget.padding != null
    ? Padding(
    padding: widget.padding,
    child: contentWidget,
    )
    : contentWidget;
    return SingleChildScrollView(
    scrollDirection: Axis.horizontal,
    child: paddedContent,
    );
    }
    }
    77 changes: 77 additions & 0 deletions example.dart
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,77 @@
    //

    import 'package:flutter/material.dart';
    import 'choice_chip_bar.dart';

    class ChoiceChipBarPage extends StatefulWidget {
    static Route<void> createRoute(RouteSettings settings) => MaterialPageRoute(
    builder: (BuildContext context) => ChoiceChipBarPage(),
    settings: settings,
    );

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

    class _ChoiceChipBarPageState extends State<ChoiceChipBarPage> {
    Hardness _selectedHardness = Hardness.medium;

    @override
    Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Scaffold(
    appBar: AppBar(
    title: Text('Choice Chip Bar'),
    ),
    body: Padding(
    padding: const EdgeInsets.symmetric(vertical: 16),
    child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: <Widget>[
    Padding(
    padding: const EdgeInsets.symmetric(horizontal: 16),
    child: Text(
    'Select type',
    style: theme.textTheme.subtitle1,
    ),
    ),
    ChoiceChipBar<Hardness>(
    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
    choices: Hardness.values,
    initialValue: _selectedHardness,
    onChanged: (Hardness selected) {
    setState(() {
    _selectedHardness = selected;
    });
    },
    choiceText: (Hardness c) {
    switch (c) {
    case Hardness.extraSoft:
    return 'Extra Soft';
    case Hardness.soft:
    return 'Soft';
    case Hardness.medium:
    return 'Medium';
    case Hardness.hard:
    return 'Hard';
    case Hardness.extraHard:
    return 'Extra Hard';
    default:
    return '<undefined>';
    }
    },
    ),
    ],
    ),
    ),
    );
    }
    }

    enum Hardness {
    extraSoft,
    soft,
    medium,
    hard,
    extraHard,
    }