Skip to content

Instantly share code, notes, and snippets.

@CoderNamedHendrick
Last active July 10, 2023 04:06
Show Gist options
  • Save CoderNamedHendrick/f599965d1f21fca6ff54f673660c132f to your computer and use it in GitHub Desktop.
Save CoderNamedHendrick/f599965d1f21fca6ff54f673660c132f to your computer and use it in GitHub Desktop.
Radio button group
name: form_widget_demo
description: A new Flutter project.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: '>=3.0.5 <4.0.0'
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
image_picker: ^1.0.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter:
uses-material-design: true
import 'package:flutter/material.dart';
enum RadioInput {
text('Text'),
widget('Widget'),
flutter('Flutter');
const RadioInput(this.title);
final String title;
}
class RadioButtonGroup extends StatefulWidget {
const RadioButtonGroup({Key? key}) : super(key: key);
@override
State<RadioButtonGroup> createState() => _RadioButtonGroupState();
}
class _RadioButtonGroupState extends State<RadioButtonGroup> {
RadioInput? groupValue;
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: RadioInput.values
.map((e) => RadioListTile(
value: e,
groupValue: groupValue,
title: Text(e.title),
onChanged: (value) {
setState(() {
groupValue = value;
});
},
))
.toList(),
);
}
}
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ImagePickerFormField(
validator: (imageFile) {
if (imageFile == null) return 'Please select an image';
return null;
},
),
const SizedBox(height: 12),
RadioButtonGroup(),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
final isValid = formKey.currentState!.validate();
if (isValid) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
backgroundColor: Colors.green,
content: Text(
'Form is valid',
style: TextStyle(color: Colors.white),
),
),
);
}
},
child: const Text('Validate Form'),
)
],
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ImagePickerFormField(
validator: (imageFile) {
if (imageFile == null) return 'Please select an image';
return null;
},
),
const SizedBox(height: 12),
RadioButtonFormGroup(
validator: (input) {
if (input == null) return 'Please select an option';
return null;
},
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
final isValid = formKey.currentState!.validate();
if (isValid) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
backgroundColor: Colors.green,
content: Text(
'Form is valid',
style: TextStyle(color: Colors.white),
),
),
);
}
},
child: const Text('Validate Form'),
)
],
)
enum RadioInput {
text('Text'),
widget('Widget'),
flutter('Flutter');
const RadioInput(this.title);
final String title;
}
class RadioButtonFormGroup extends FormField<RadioInput?> {
RadioButtonFormGroup({
super.key,
RadioGroupInputController? controller,
ValueChanged<RadioInput?>? onChanged,
super.validator,
super.autovalidateMode,
}) : super(
initialValue: controller?.value,
builder: (state) {
void onChangedHandler(RadioInput? value) {
state.didChange(value);
if (onChanged != null) {
onChanged(value);
}
}
return UnmanagedRestorationScope(
bucket: state.bucket,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
RadioButtonGroup(
controller: controller,
onChanged: onChangedHandler,
fillColor: state.hasError ? Colors.red : null,
),
if (state.hasError) ...[
Text(
state.errorText!,
style: const TextStyle(fontSize: 15, color: Colors.red),
),
],
],
),
),
);
},
);
}
class RadioButtonGroup extends StatefulWidget {
const RadioButtonGroup({
Key? key,
this.controller,
this.onChanged,
this.fillColor,
}) : super(key: key);
final RadioGroupInputController? controller;
final ValueChanged<RadioInput?>? onChanged;
final Color? fillColor;
@override
State<RadioButtonGroup> createState() => _RadioButtonGroupState();
}
class _RadioButtonGroupState extends State<RadioButtonGroup> {
late RadioGroupInputController controller;
@override
void initState() {
super.initState();
controller = widget.controller ?? RadioGroupInputController();
controller.addListener(() {
widget.onChanged?.call(controller.value);
});
}
@override
void didUpdateWidget(covariant RadioButtonGroup oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.controller != widget.controller) {
controller = widget.controller ?? RadioGroupInputController();
}
}
@override
void dispose() {
if (widget.controller == null) controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: controller,
builder: (_, radioGroupValue, __) => Column(
mainAxisSize: MainAxisSize.min,
children: RadioInput.values
.map((e) => RadioListTile(
value: e,
groupValue: radioGroupValue,
fillColor: switch (widget.fillColor) {
final color? => MaterialStateProperty.all(color),
_ => null,
},
title: Text(e.title),
onChanged: (value) {
controller.input = value;
},
))
.toList(),
),
);
}
}
class RadioGroupInputController extends ValueNotifier<RadioInput?> {
RadioGroupInputController({RadioInput? initialValue}) : super(initialValue);
String get title => value?.title ?? '';
set input(RadioInput? newInput) {
if (newInput == value) null;
value = newInput;
notifyListeners();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment