274 lines
6.6 KiB
Dart
274 lines
6.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:mobile_monitoring/core/constants/colors.dart';
|
|
|
|
/// Custom Switch - Switch widget yang reusable
|
|
class CustomSwitch extends StatelessWidget {
|
|
final bool value;
|
|
final ValueChanged<bool>? onChanged;
|
|
final Color? activeColor;
|
|
final Color? inactiveColor;
|
|
|
|
const CustomSwitch({
|
|
super.key,
|
|
required this.value,
|
|
this.onChanged,
|
|
this.activeColor,
|
|
this.inactiveColor,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Switch(
|
|
value: value,
|
|
onChanged: onChanged,
|
|
activeTrackColor: activeColor ?? AppColors.primary,
|
|
inactiveThumbColor: inactiveColor ?? AppColors.mediumGray,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Labeled Switch - Switch dengan label
|
|
class LabeledSwitch extends StatelessWidget {
|
|
final String label;
|
|
final bool value;
|
|
final ValueChanged<bool>? onChanged;
|
|
final String? description;
|
|
final IconData? icon;
|
|
|
|
const LabeledSwitch({
|
|
super.key,
|
|
required this.label,
|
|
required this.value,
|
|
this.onChanged,
|
|
this.description,
|
|
this.icon,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return InkWell(
|
|
onTap: onChanged != null ? () => onChanged!(!value) : null,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
|
child: Row(
|
|
children: [
|
|
if (icon != null) ...[
|
|
Icon(
|
|
icon,
|
|
color: AppColors.textSecondary,
|
|
size: 24,
|
|
),
|
|
const SizedBox(width: 12),
|
|
],
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
label,
|
|
style: const TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w500,
|
|
color: AppColors.textPrimary,
|
|
),
|
|
),
|
|
if (description != null) ...[
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
description!,
|
|
style: const TextStyle(
|
|
fontSize: 12,
|
|
color: AppColors.textSecondary,
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
CustomSwitch(
|
|
value: value,
|
|
onChanged: onChanged,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Custom Checkbox - Checkbox yang reusable
|
|
class CustomCheckbox extends StatelessWidget {
|
|
final bool value;
|
|
final ValueChanged<bool?>? onChanged;
|
|
final Color? activeColor;
|
|
|
|
const CustomCheckbox({
|
|
super.key,
|
|
required this.value,
|
|
this.onChanged,
|
|
this.activeColor,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Checkbox(
|
|
value: value,
|
|
onChanged: onChanged,
|
|
activeColor: activeColor ?? AppColors.primary,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Labeled Checkbox - Checkbox dengan label
|
|
class LabeledCheckbox extends StatelessWidget {
|
|
final String label;
|
|
final bool value;
|
|
final ValueChanged<bool?>? onChanged;
|
|
|
|
const LabeledCheckbox({
|
|
super.key,
|
|
required this.label,
|
|
required this.value,
|
|
this.onChanged,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return InkWell(
|
|
onTap: onChanged != null ? () => onChanged!(!value) : null,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
child: Row(
|
|
children: [
|
|
CustomCheckbox(
|
|
value: value,
|
|
onChanged: onChanged,
|
|
),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Text(
|
|
label,
|
|
style: const TextStyle(
|
|
fontSize: 14,
|
|
color: AppColors.textPrimary,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Custom Radio - Radio button yang reusable
|
|
class CustomRadio<T> extends StatefulWidget {
|
|
final T value;
|
|
final T initialGroupValue;
|
|
final ValueChanged<T>? onChanged;
|
|
final Color? activeColor;
|
|
|
|
const CustomRadio({
|
|
super.key,
|
|
required this.value,
|
|
required this.initialGroupValue,
|
|
this.onChanged,
|
|
this.activeColor,
|
|
});
|
|
|
|
@override
|
|
State<CustomRadio<T>> createState() => _CustomRadioState<T>();
|
|
}
|
|
|
|
class _CustomRadioState<T> extends State<CustomRadio<T>> {
|
|
late T _groupValue;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_groupValue = widget.initialGroupValue;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return GestureDetector(
|
|
onTap: widget.onChanged != null ? () {
|
|
setState(() {
|
|
_groupValue = widget.value;
|
|
});
|
|
widget.onChanged!(widget.value);
|
|
} : null,
|
|
child: Container(
|
|
width: 20,
|
|
height: 20,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
border: Border.all(
|
|
color: _groupValue == widget.value
|
|
? (widget.activeColor ?? AppColors.primary)
|
|
: AppColors.borderDark,
|
|
width: 2,
|
|
),
|
|
),
|
|
child: _groupValue == widget.value
|
|
? Center(
|
|
child: Container(
|
|
width: 10,
|
|
height: 10,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: widget.activeColor ?? AppColors.primary,
|
|
),
|
|
),
|
|
)
|
|
: null,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Labeled Radio - Radio button dengan label
|
|
class LabeledRadio<T> extends StatelessWidget {
|
|
final String label;
|
|
final T value;
|
|
final T initialGroupValue;
|
|
final ValueChanged<T>? onChanged;
|
|
|
|
const LabeledRadio({
|
|
super.key,
|
|
required this.label,
|
|
required this.value,
|
|
required this.initialGroupValue,
|
|
this.onChanged,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return InkWell(
|
|
onTap: onChanged != null ? () => onChanged!(value) : null,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
child: Row(
|
|
children: [
|
|
CustomRadio<T>(
|
|
value: value,
|
|
initialGroupValue: initialGroupValue,
|
|
onChanged: onChanged,
|
|
),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Text(
|
|
label,
|
|
style: const TextStyle(
|
|
fontSize: 14,
|
|
color: AppColors.textPrimary,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|