Merge pull request #29 from Iconica-Development/feature/multiple_bool_input

feat: Add checkbox as option to the flutter input switch which is now…
This commit is contained in:
Gorter-dev 2024-02-06 10:48:38 +01:00 committed by GitHub
commit 7024fb7e40
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 165 additions and 87 deletions

View file

@ -62,5 +62,7 @@
* Addition of 'decoration' parameter to 'FlutterFormInputPassword' * Addition of 'decoration' parameter to 'FlutterFormInputPassword'
## 2.7.1 ## 2.7.1
* Added Iconica CI and Iconica Linter
* Added Iconica CI and Iconica Linter ## 3.0.0
* Updated the FlutterFormInputSwitch to FlutterFormInputBool. This now includes a parameter to either show a checkbox or switch

View file

@ -54,8 +54,8 @@ class _MyHomePageState extends State<MyHomePage> {
child: Column( child: Column(
children: [ children: [
Container(height: 10), Container(height: 10),
const Text('FlutterFormInputSwitch'), const Text('FlutterFormInputBool'),
FlutterFormInputSwitch( FlutterFormInputBool(
initialValue: true, initialValue: true,
onChanged: (v) { onChanged: (v) {
debugPrint('Switch changed to $v'); debugPrint('Switch changed to $v');

View file

@ -68,7 +68,7 @@ packages:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "2.7.0" version: "3.0.0"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:

View file

@ -6,10 +6,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_input_library/src/inputs/switch/switch_field.dart'; import 'package:flutter_input_library/src/inputs/bool/bool_field.dart';
class FlutterFormInputSwitch extends StatelessWidget { class FlutterFormInputBool extends StatelessWidget {
const FlutterFormInputSwitch({ const FlutterFormInputBool({
super.key, super.key,
this.label, this.label,
this.onSaved, this.onSaved,
@ -17,20 +17,35 @@ class FlutterFormInputSwitch extends StatelessWidget {
this.onChanged, this.onChanged,
this.focusNode, this.focusNode,
this.initialValue = false, this.initialValue = false,
this.widgetType = BoolWidgetType.switchWidget,
this.leftWidget,
this.rightWidget,
}); });
final Widget? label; final Widget? label;
final Function(bool?)? onSaved; final Function(bool?)? onSaved;
final String? Function(bool?)? validator; final String? Function(bool?)? validator;
final Function(bool?)? onChanged; final Function(bool?)? onChanged;
final bool? initialValue; final bool? initialValue;
final FocusNode? focusNode; final FocusNode? focusNode;
final BoolWidgetType widgetType;
final Widget? leftWidget;
final Widget? rightWidget;
@override @override
Widget build(BuildContext context) => SwitchFormField( Widget build(BuildContext context) => BoolFormField(
onSaved: (value) => onSaved?.call(value), onSaved: (value) => onSaved?.call(value),
onChanged: (value) => onChanged?.call(value), onChanged: (value) => onChanged?.call(value),
validator: (value) => validator?.call(value), validator: (value) => validator?.call(value),
initialValue: initialValue ?? false, initialValue: initialValue ?? false,
focusNode: focusNode, focusNode: focusNode,
widgetType: widgetType,
leftWidget: leftWidget,
rightWidget: rightWidget,
); );
} }
enum BoolWidgetType {
switchWidget,
checkbox,
}

View file

@ -0,0 +1,123 @@
// SPDX-FileCopyrightText: 2022 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
// ignore_for_file: avoid_positional_boolean_parameters
import 'package:flutter/material.dart';
import 'package:flutter_input_library/flutter_input_library.dart';
class BoolFormField extends FormField<bool> {
BoolFormField({
required FormFieldSetter<bool> super.onSaved,
required FormFieldValidator<bool> super.validator,
super.key,
FocusNode? focusNode,
bool super.initialValue = false,
void Function(bool? value)? onChanged,
BoolWidgetType widgetType = BoolWidgetType.switchWidget,
Widget? leftWidget,
Widget? rightWidget,
}) : super(
builder: (FormFieldState<bool> state) => BoolWidget(
initialValue: initialValue,
state: state,
focusNode: focusNode,
onChanged: onChanged,
widgetType: widgetType,
errorText: state.errorText,
leftWidget: leftWidget,
rightWidget: rightWidget,
),
);
}
class BoolWidget extends StatefulWidget {
const BoolWidget({
required this.state,
this.initialValue = false,
this.onChanged,
this.focusNode,
this.widgetType = BoolWidgetType.switchWidget,
this.errorText,
this.leftWidget,
this.rightWidget,
super.key,
});
final bool initialValue;
final FormFieldState<bool> state;
final FocusNode? focusNode;
final void Function(bool? value)? onChanged;
final BoolWidgetType widgetType;
final String? errorText;
final Widget? leftWidget;
final Widget? rightWidget;
@override
State<BoolWidget> createState() => _BoolWidgetState();
}
class _BoolWidgetState extends State<BoolWidget> {
late Widget child;
late bool value = widget.initialValue;
void onChanged(bool value) {
widget.onChanged?.call(value);
widget.state.didChange(value);
setState(() {
this.value = value;
});
}
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
late Widget child;
switch (widget.widgetType) {
case BoolWidgetType.switchWidget:
child = Switch(
value: value,
focusNode: widget.focusNode,
onChanged: onChanged,
);
break;
case BoolWidgetType.checkbox:
child = Checkbox(
value: value,
focusNode: widget.focusNode,
onChanged: (bool? value) {
if (value != null) {
onChanged(value);
}
},
);
break;
}
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
widget.leftWidget ?? const SizedBox.shrink(),
child,
widget.rightWidget ?? const SizedBox.shrink(),
],
),
if (widget.errorText != null) ...[
Text(
widget.errorText!,
style: theme.inputDecorationTheme.errorStyle,
),
],
],
);
}
}

View file

@ -2,17 +2,17 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
/// Converts an index of a set size to the corresponding index of a collection /// Converts an index of a set size to the corresponding index of a collection
/// of another size as if they were circular. /// of another size as if they were circular.
/// ///
/// Takes a [position] from collection Foo, a [base] from where Foo's index /// Takes a [position] from collection Foo, a [base] from where Foo's index
/// originated and the [length] of a second collection Baa, for which the /// originated and the [length] of a second collection Baa, for which the
/// correlating index is sought. /// correlating index is sought.
/// ///
/// For example; We have a Carousel of 10000(simulating infinity) but only 6 /// For example; We have a Carousel of 10000(simulating infinity) but only 6
/// images. We need to repeat the images to give the illusion of a never /// images. We need to repeat the images to give the illusion of a never
/// ending stream. By calling [getRealIndex] with position and base we get /// ending stream. By calling [getRealIndex] with position and base we get
/// an offset. This offset modulo our length, 6, will return a number between /// an offset. This offset modulo our length, 6, will return a number between
/// 0 and 5, which represent the image to be placed in the given position. /// 0 and 5, which represent the image to be placed in the given position.
int getRealIndex(int position, int base, int? length) { int getRealIndex(int position, int base, int? length) {
var offset = position - base; var offset = position - base;

View file

@ -1,8 +1,8 @@
export 'bool/bool.dart';
export 'carousel/carousel.dart'; export 'carousel/carousel.dart';
export 'date_picker/date_picker.dart'; export 'date_picker/date_picker.dart';
export 'number_picker/number_picker.dart'; export 'number_picker/number_picker.dart';
export 'scroll_picker/scroll_picker.dart'; export 'scroll_picker/scroll_picker.dart';
export 'slider/slider.dart'; export 'slider/slider.dart';
export 'switch/switch.dart';
export 'text/password.dart'; export 'text/password.dart';
export 'text/plain_text.dart'; export 'text/plain_text.dart';

View file

@ -45,11 +45,11 @@ class DecimalNumberPicker extends StatelessWidget {
final TextMapper? decimalTextMapper; final TextMapper? decimalTextMapper;
final bool integerZeroPad; final bool integerZeroPad;
/// Decoration to apply to central box where the selected integer value /// Decoration to apply to central box where the selected integer value
/// is placed /// is placed
final Decoration? integerDecoration; final Decoration? integerDecoration;
/// Decoration to apply to central box where the selected decimal value /// Decoration to apply to central box where the selected decimal value
/// is placed /// is placed
final Decoration? decimalDecoration; final Decoration? decimalDecoration;

View file

@ -159,7 +159,7 @@ class InfiniteListViewState extends State<InfiniteListView> {
physics: scrollPhysics, physics: scrollPhysics,
viewportBuilder: (BuildContext context, ViewportOffset offset) => Builder( viewportBuilder: (BuildContext context, ViewportOffset offset) => Builder(
builder: (BuildContext context) { builder: (BuildContext context) {
/// Build negative [ScrollPosition] for the negative scrolling /// Build negative [ScrollPosition] for the negative scrolling
/// [Viewport]. /// [Viewport].
var state = Scrollable.of(context); var state = Scrollable.of(context);
var negativeOffset = _InfiniteScrollPosition( var negativeOffset = _InfiniteScrollPosition(
@ -170,13 +170,13 @@ class InfiniteListViewState extends State<InfiniteListView> {
negativeScroll: true, negativeScroll: true,
); );
/// Keep the negative scrolling [Viewport] positioned to the /// Keep the negative scrolling [Viewport] positioned to the
/// [ScrollPosition]. /// [ScrollPosition].
offset.addListener(() { offset.addListener(() {
negativeOffset._forceNegativePixels(offset.pixels); negativeOffset._forceNegativePixels(offset.pixels);
}); });
/// Stack the two [Viewport]s on top of each other so they move in /// Stack the two [Viewport]s on top of each other so they move in
/// sync. /// sync.
return Stack( return Stack(
children: <Widget>[ children: <Widget>[
@ -315,7 +315,7 @@ class InfiniteListViewState extends State<InfiniteListView> {
} }
} }
/// Same as a [ScrollController] except it provides [ScrollPosition] objects /// Same as a [ScrollController] except it provides [ScrollPosition] objects
/// with infinite bounds. /// with infinite bounds.
class InfiniteScrollController extends ScrollController { class InfiniteScrollController extends ScrollController {
/// Creates a new [InfiniteScrollController] /// Creates a new [InfiniteScrollController]

View file

@ -1,62 +0,0 @@
// SPDX-FileCopyrightText: 2022 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart';
class SwitchFormField extends FormField<bool> {
SwitchFormField({
required FormFieldSetter<bool> super.onSaved,
required FormFieldValidator<bool> super.validator,
super.key,
FocusNode? focusNode,
bool super.initialValue = false,
// ignore: avoid_positional_boolean_parameters
void Function(bool? value)? onChanged,
}) : super(
builder: (FormFieldState<bool> state) => SwitchWidget(
initialValue: initialValue,
state: state,
focusNode: focusNode,
onChanged: onChanged,
),
);
}
class SwitchWidget extends StatefulWidget {
const SwitchWidget({
required this.state,
this.initialValue = false,
this.onChanged,
this.focusNode,
super.key,
});
final bool initialValue;
final FormFieldState<bool> state;
final FocusNode? focusNode;
// ignore: avoid_positional_boolean_parameters
final void Function(bool? value)? onChanged;
@override
State<SwitchWidget> createState() => _SwitchWidgetState();
}
class _SwitchWidgetState extends State<SwitchWidget> {
late bool value = widget.initialValue;
@override
Widget build(BuildContext context) => Switch(
value: value,
focusNode: widget.focusNode,
onChanged: (bool value) {
widget.onChanged?.call(value);
widget.state.didChange(value);
setState(() {
this.value = value;
});
},
);
}

View file

@ -5,8 +5,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
/// Generates a [TextFormField] for passwords. It requires a /// Generates a [TextFormField] for passwords. It requires a
/// [FlutterFormInputController] as the [controller] parameter and an /// [FlutterFormInputController] as the [controller] parameter and an
/// optional [Widget] as [label] /// optional [Widget] as [label]
class FlutterFormInputPassword extends StatefulWidget { class FlutterFormInputPassword extends StatefulWidget {
const FlutterFormInputPassword({ const FlutterFormInputPassword({

View file

@ -1,6 +1,6 @@
name: flutter_input_library name: flutter_input_library
description: A new Flutter package project. description: A new Flutter package project.
version: 2.7.1 version: 3.0.0
repository: https://github.com/Iconica-Development/flutter_input_library repository: https://github.com/Iconica-Development/flutter_input_library
environment: environment: