From 49c77f2b6273f42860b5b3613a88a71382851803 Mon Sep 17 00:00:00 2001 From: Ketan Choyal Date: Fri, 31 Mar 2023 10:25:38 -0400 Subject: [PATCH] feat: Added feature to select button programmatically --- CHANGELOG.md | 3 + README.md | 22 +++- example/lib/RadioButtonPage.dart | 41 +++++-- example/pubspec.lock | 2 +- lib/CustomButtons/CustomCheckBoxGroup.dart | 93 ++++++++------- lib/CustomButtons/CustomRadioButton.dart | 131 +++++++++++---------- pubspec.yaml | 2 +- 7 files changed, 170 insertions(+), 124 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cab82ac..af57471 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## [2.2.0] - 31 March 2023 +- Added way to select buttons programmatically (Fix #69) +- Fixed bug where buttons were not being selected when value and label were different ## [2.1.3] - 22 March 2023 - Added Feature to disable a button (Fix #67) diff --git a/README.md b/README.md index dd161c6..d6b6bb7 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,6 @@ Custom Radio Buttons and Grouped Check Box Button Custom Flutter widgets that makes Checkbox and Radio Buttons much cleaner and easier -## Breaking Changes: - From Version 1.0.2 - - buttonColor is now unSelectedColor - ## Installing Add the following to your `pubspec.yaml` file: @@ -87,6 +82,23 @@ Add the following to your `pubspec.yaml` file: padding: 10, ); +# Changing values Programiically + +You can acces the widget's state using `Key` now + +Example: Create a key for the CustomRadioButton widget + + final key = new GlobalKey>(); + +now to change the value of the widget pass the value to the `selectButton` method + + key.currentState.selectButton(); + +Similarly for the CustomCheckBoxGroup widget + + final key = new GlobalKey>(); + + key.currentState.selectButton(); ## Screenshots diff --git a/example/lib/RadioButtonPage.dart b/example/lib/RadioButtonPage.dart index 9f47fa7..782bc40 100644 --- a/example/lib/RadioButtonPage.dart +++ b/example/lib/RadioButtonPage.dart @@ -1,8 +1,15 @@ import 'package:custom_radio_grouped_button/custom_radio_grouped_button.dart'; import 'package:flutter/material.dart'; -class RadioButton extends StatelessWidget { - const RadioButton({Key key}) : super(key: key); +class RadioButton extends StatefulWidget { + RadioButton({Key key}) : super(key: key); + + @override + State createState() => _RadioButtonState(); +} + +class _RadioButtonState extends State { + final key = new GlobalKey>(); @override Widget build(BuildContext context) { @@ -11,12 +18,27 @@ class RadioButton extends StatelessWidget { title: Text('Radio Button Example'), centerTitle: true, ), - floatingActionButton: FloatingActionButton.extended( - label: Text('Grouped Button'), - onPressed: () { - Navigator.pop(context); - }, - icon: Icon(Icons.check_box), + floatingActionButton: Padding( + padding: const EdgeInsets.only(left: 30.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + FloatingActionButton( + heroTag: "sfsdf", + onPressed: () { + key.currentState.selectButton('jhgiugx'); + }, + child: Icon(Icons.check_box_outline_blank), + ), + FloatingActionButton.extended( + label: Text('Grouped Button'), + onPressed: () { + Navigator.pop(context); + }, + icon: Icon(Icons.check_box), + ), + ], + ), ), body: Container( child: ListView( @@ -47,7 +69,8 @@ class RadioButton extends StatelessWidget { SizedBox( height: 10, ), - CustomRadioButton( + CustomRadioButton( + key: key, horizontal: true, unSelectedColor: Theme.of(context).canvasColor, disabledValues: [ diff --git a/example/pubspec.lock b/example/pubspec.lock index 79897e3..b1c9b7f 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -55,7 +55,7 @@ packages: path: ".." relative: true source: path - version: "2.1.3" + version: "2.2.0" fake_async: dependency: transitive description: diff --git a/lib/CustomButtons/CustomCheckBoxGroup.dart b/lib/CustomButtons/CustomCheckBoxGroup.dart index e5dea34..39eec87 100644 --- a/lib/CustomButtons/CustomCheckBoxGroup.dart +++ b/lib/CustomButtons/CustomCheckBoxGroup.dart @@ -122,14 +122,14 @@ class CustomCheckBoxGroup extends StatefulWidget { /// Radius for shape radio button final double shapeRadius; - _CustomCheckBoxGroupState createState() => _CustomCheckBoxGroupState(); + CustomCheckBoxGroupState createState() => CustomCheckBoxGroupState(); } -class _CustomCheckBoxGroupState extends State { - List selectedLables = []; +class CustomCheckBoxGroupState extends State> { + List selectedValues = []; - Color borderColor(e) => - (selectedLables.contains(e) + Color _borderColor(T e) => + (selectedValues.contains(e) ? widget.selectedBorderColor : widget.unSelectedBorderColor) ?? Theme.of(context).primaryColor; @@ -140,14 +140,33 @@ class _CustomCheckBoxGroupState extends State { widget.defaultSelected?.where((e) { return widget.buttonValuesList.contains(e); }).forEach((e) { - selectedLables.add(e); + selectedValues.add(e); }); } + List get buttonValuesList => widget.buttonValuesList; + + List get buttonLables => widget.buttonLables; + + List get disabledValues => widget.disabledValues; + + /// This function will select the button and update the state + /// THis can be access from outside to change the selected value programatically + /// Please note that this will also call the [checkBoxButtonValues] callback + void selectButton(T selectedValue) { + if (selectedValues.contains(selectedValue)) { + selectedValues.remove(selectedValue); + } else { + selectedValues.add(selectedValue); + } + if (mounted) setState(() {}); + widget.checkBoxButtonValues(selectedValues); + } + List _buildButtonsColumn() { - return widget.buttonValuesList.map((e) { - bool disabled = widget.disabledValues.contains(e); - int index = widget.buttonValuesList.indexOf(e); + return buttonValuesList.map((e) { + bool disabled = disabledValues.contains(e); + int index = buttonValuesList.indexOf(e); return Padding( padding: EdgeInsets.all(widget.padding), child: Card( @@ -155,7 +174,7 @@ class _CustomCheckBoxGroupState extends State { EdgeInsets.all(widget.absoluteZeroSpacing ? 0 : 4), color: disabled ? widget.disabledColor ?? widget.unSelectedColor - : selectedLables.contains(e) + : selectedValues.contains(e) ? widget.selectedColor : widget.unSelectedColor, elevation: widget.elevation, @@ -174,26 +193,16 @@ class _CustomCheckBoxGroupState extends State { ? widget.customShape == null ? OutlineInputBorder( borderSide: - BorderSide(color: borderColor(e), width: 1), + BorderSide(color: _borderColor(e), width: 1), borderRadius: BorderRadius.all(Radius.circular(widget.radius)), ) : widget.customShape : OutlineInputBorder( - borderSide: BorderSide(color: borderColor(e), width: 1), + borderSide: BorderSide(color: _borderColor(e), width: 1), borderRadius: BorderRadius.zero, ), - onPressed: disabled - ? null - : () { - if (selectedLables.contains(e)) { - selectedLables.remove(e); - } else { - selectedLables.add(e); - } - setState(() {}); - widget.checkBoxButtonValues(selectedLables); - }, + onPressed: disabled ? null : () => selectButton(e), child: Text( widget.buttonLables[index], textAlign: TextAlign.center, @@ -202,7 +211,7 @@ class _CustomCheckBoxGroupState extends State { style: widget.buttonTextStyle.textStyle.copyWith( color: disabled ? widget.buttonTextStyle.disabledColor - : selectedLables.contains(e) + : selectedValues.contains(e) ? widget.buttonTextStyle.selectedColor : widget.buttonTextStyle.unSelectedColor, ), @@ -215,14 +224,17 @@ class _CustomCheckBoxGroupState extends State { } List _buildButtonsRow() { - return widget.buttonValuesList.map((e) { - int index = widget.buttonValuesList.indexOf(e); + return buttonValuesList.map((e) { + int index = buttonValuesList.indexOf(e); + bool disabled = disabledValues.contains(e); return Card( margin: widget.margin ?? EdgeInsets.all(widget.absoluteZeroSpacing ? 0 : 4), - color: selectedLables.contains(e) - ? widget.selectedColor - : widget.unSelectedColor, + color: disabled + ? widget.disabledColor ?? widget.unSelectedColor + : selectedValues.contains(e) + ? widget.selectedColor + : widget.unSelectedColor, elevation: widget.elevation, shape: widget.enableShape ? widget.customShape == null @@ -240,33 +252,28 @@ class _CustomCheckBoxGroupState extends State { shape: widget.enableShape ? widget.customShape == null ? OutlineInputBorder( - borderSide: BorderSide(color: borderColor(e), width: 1), + borderSide: + BorderSide(color: _borderColor(e), width: 1), borderRadius: BorderRadius.all(Radius.circular(widget.radius)), ) : widget.customShape : OutlineInputBorder( - borderSide: BorderSide(color: borderColor(e), width: 1), + borderSide: BorderSide(color: _borderColor(e), width: 1), borderRadius: BorderRadius.zero, ), - onPressed: () { - if (selectedLables.contains(e)) { - selectedLables.remove(e); - } else { - selectedLables.add(e); - } - setState(() {}); - widget.checkBoxButtonValues(selectedLables); - }, + onPressed: disabled ? null : () => selectButton(e), child: Text( widget.buttonLables[index], textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, maxLines: 1, style: widget.buttonTextStyle.textStyle.copyWith( - color: selectedLables.contains(e) - ? widget.buttonTextStyle.selectedColor - : widget.buttonTextStyle.unSelectedColor, + color: disabled + ? widget.buttonTextStyle.disabledColor + : selectedValues.contains(e) + ? widget.buttonTextStyle.selectedColor + : widget.buttonTextStyle.unSelectedColor, ), ), ), diff --git a/lib/CustomButtons/CustomRadioButton.dart b/lib/CustomButtons/CustomRadioButton.dart index df884ae..cdaa5b5 100644 --- a/lib/CustomButtons/CustomRadioButton.dart +++ b/lib/CustomButtons/CustomRadioButton.dart @@ -126,14 +126,14 @@ class CustomRadioButton extends StatefulWidget { /// Radius for shape radio button final double shapeRadius; - _CustomRadioButtonState createState() => _CustomRadioButtonState(); + CustomRadioButtonState createState() => CustomRadioButtonState(); } -class _CustomRadioButtonState extends State> { - String? _currentSelectedLabel; +class CustomRadioButtonState extends State> { + T? _currentSelectedValue; - Color borderColor(index) => - (_currentSelectedLabel == widget.buttonLables[index] + Color _borderColor(T e) => + (_currentSelectedValue == e ? widget.selectedBorderColor : widget.unSelectedBorderColor) ?? Theme.of(context).primaryColor; @@ -143,17 +143,31 @@ class _CustomRadioButtonState extends State> { super.initState(); if (widget.defaultSelected != null) { if (widget.buttonValues.contains(widget.defaultSelected)) { - int index = widget.buttonValues.indexOf(widget.defaultSelected!); - _currentSelectedLabel = widget.buttonLables[index]; + _currentSelectedValue = widget.defaultSelected; } else throw Exception("Default Value not found in button value list"); } } + List get buttonValues => widget.buttonValues; + + List get buttonLables => widget.buttonLables; + + List get disabledValues => widget.disabledValues; + + /// This function will select the button and update the state + /// THis can be access from outside to change the selected value programatically + /// Please note that this will all call the [radioButtonValue] callback + void selectButton(T selectedValue) { + widget.radioButtonValue(selectedValue); + if (mounted) setState(() {}); + _currentSelectedValue = selectedValue; + } + List _buildButtonsColumn() { - return widget.buttonValues.map((e) { - int index = widget.buttonValues.indexOf(e); - bool disabled = widget.disabledValues.contains(e); + return buttonValues.map((e) { + int index = buttonValues.indexOf(e); + bool disabled = disabledValues.contains(e); return Padding( padding: EdgeInsets.all(widget.padding), child: Card( @@ -161,43 +175,33 @@ class _CustomRadioButtonState extends State> { EdgeInsets.all(widget.absoluteZeroSpacing ? 0 : 4), color: disabled ? widget.disabledColor ?? widget.unSelectedColor - : _currentSelectedLabel == widget.buttonLables[index] + : _currentSelectedValue == e ? widget.selectedColor : widget.unSelectedColor, elevation: widget.elevation, shape: widget.enableShape - ? widget.customShape == null - ? RoundedRectangleBorder( - borderRadius: - BorderRadius.all(Radius.circular(widget.shapeRadius)), - ) - : widget.customShape + ? widget.customShape ?? + RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(widget.shapeRadius)), + ) : null, child: Container( height: widget.height, child: MaterialButton( shape: widget.enableShape - ? widget.customShape == null - ? OutlineInputBorder( - borderSide: - BorderSide(color: borderColor(index), width: 1), - borderRadius: - BorderRadius.all(Radius.circular(widget.radius)), - ) - : widget.customShape + ? widget.customShape ?? + OutlineInputBorder( + borderSide: + BorderSide(color: _borderColor(e), width: 1), + borderRadius: + BorderRadius.all(Radius.circular(widget.radius)), + ) : OutlineInputBorder( - borderSide: - BorderSide(color: borderColor(index), width: 1), + borderSide: BorderSide(color: _borderColor(e), width: 1), borderRadius: BorderRadius.zero, ), - onPressed: disabled - ? null - : () { - widget.radioButtonValue(e); - setState(() { - _currentSelectedLabel = widget.buttonLables[index]; - }); - }, + onPressed: disabled ? null : () => selectButton(e), child: Text( widget.buttonLables[index], textAlign: TextAlign.center, @@ -206,7 +210,7 @@ class _CustomRadioButtonState extends State> { style: widget.buttonTextStyle.textStyle.copyWith( color: disabled ? widget.buttonTextStyle.disabledColor - : _currentSelectedLabel == widget.buttonLables[index] + : _currentSelectedValue == e ? widget.buttonTextStyle.selectedColor : widget.buttonTextStyle.unSelectedColor, ), @@ -219,22 +223,24 @@ class _CustomRadioButtonState extends State> { } List _buildButtonsRow() { - return widget.buttonValues.map((e) { - int index = widget.buttonValues.indexOf(e); + return buttonValues.map((e) { + int index = buttonValues.indexOf(e); + bool disabled = disabledValues.contains(e); return Card( margin: widget.margin ?? EdgeInsets.all(widget.absoluteZeroSpacing ? 0 : 4), - color: _currentSelectedLabel == widget.buttonLables[index] - ? widget.selectedColor - : widget.unSelectedColor, + color: disabled + ? widget.disabledColor ?? widget.unSelectedColor + : _currentSelectedValue == e + ? widget.selectedColor + : widget.unSelectedColor, elevation: widget.elevation, shape: widget.enableShape - ? widget.customShape == null - ? RoundedRectangleBorder( - borderRadius: - BorderRadius.all(Radius.circular(widget.shapeRadius)), - ) - : widget.customShape + ? widget.customShape ?? + RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(widget.shapeRadius)), + ) : null, child: Container( height: widget.height, @@ -242,33 +248,28 @@ class _CustomRadioButtonState extends State> { constraints: widget.autoWidth ? null : BoxConstraints(maxWidth: 250), child: MaterialButton( shape: widget.enableShape - ? widget.customShape == null - ? OutlineInputBorder( - borderSide: - BorderSide(color: borderColor(index), width: 1), - borderRadius: - BorderRadius.all(Radius.circular(widget.radius)), - ) - : widget.customShape + ? widget.customShape ?? + OutlineInputBorder( + borderSide: BorderSide(color: _borderColor(e), width: 1), + borderRadius: + BorderRadius.all(Radius.circular(widget.radius)), + ) : OutlineInputBorder( - borderSide: BorderSide(color: borderColor(index), width: 1), + borderSide: BorderSide(color: _borderColor(e), width: 1), borderRadius: BorderRadius.zero, ), - onPressed: () { - widget.radioButtonValue(e); - setState(() { - _currentSelectedLabel = widget.buttonLables[index]; - }); - }, + onPressed: disabled ? null : () => selectButton(e), child: Text( widget.buttonLables[index], textAlign: TextAlign.left, overflow: TextOverflow.ellipsis, maxLines: 1, style: widget.buttonTextStyle.textStyle.copyWith( - color: _currentSelectedLabel == widget.buttonLables[index] - ? widget.buttonTextStyle.selectedColor - : widget.buttonTextStyle.unSelectedColor, + color: disabled + ? widget.buttonTextStyle.disabledColor + : _currentSelectedValue == e + ? widget.buttonTextStyle.selectedColor + : widget.buttonTextStyle.unSelectedColor, ), ), ), diff --git a/pubspec.yaml b/pubspec.yaml index 7786ead..5d1543a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: custom_radio_grouped_button description: Custom Radio Buttons and Grouped Check Box Button -version: 2.1.3 +version: 2.2.0 homepage: https://github.com/ketanchoyal/custom_radio_grouped_button