wip: buat input component lib
# besok lanjut forms library (reactive form)
This commit is contained in:
parent
47c9386eb2
commit
c9e784fb73
6
makefile
6
makefile
|
@ -2,6 +2,12 @@ PACKAGES := $(wildcard packages/*)
|
|||
FEATURES := $(wildcard packages/features/*)
|
||||
BUILD-RUNNER := packages/golektruk_api
|
||||
|
||||
run:
|
||||
flutter run lib/main.dart
|
||||
|
||||
story:
|
||||
flutter run packages/component_library/example/main.dart
|
||||
|
||||
intl:
|
||||
flutter gen-l10n
|
||||
|
||||
|
|
|
@ -1,9 +1,57 @@
|
|||
import 'package:component_library/component_library.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:storybook_flutter/storybook_flutter.dart';
|
||||
|
||||
List<Story> getStories(GolekThemeData theme) {
|
||||
return [
|
||||
Story(
|
||||
name: 'Plain Input Field',
|
||||
section: 'Form Input',
|
||||
builder: (_, k) => InputField(
|
||||
errorMessage: k.text(label: 'errorMessage'),
|
||||
placeholder: k.text(
|
||||
label: 'placeholder',
|
||||
initial: 'Placeholder',
|
||||
),
|
||||
label: k.text(label: 'Label', initial: 'Label'),
|
||||
obscureText: k.boolean(label: 'obscureText', initial: false),
|
||||
textInputType: k.options<TextInputType>(
|
||||
label: 'textInputType',
|
||||
initial: TextInputType.text,
|
||||
options: [
|
||||
const Option('Email', TextInputType.emailAddress),
|
||||
const Option('Number', TextInputType.number),
|
||||
const Option('Date Time', TextInputType.datetime),
|
||||
]),
|
||||
inputFormatters: k.options<List<TextInputFormatter>>(
|
||||
label: 'inputFormatters',
|
||||
initial: [],
|
||||
options: [
|
||||
Option(
|
||||
'Maximum Text Input Formatter',
|
||||
[LengthLimitingTextInputFormatter(12)],
|
||||
),
|
||||
Option(
|
||||
'Regex Filter Text Input Formatter',
|
||||
[FilteringTextInputFormatter.allow(RegExp(r'^[2-9][0-9]*'))],
|
||||
),
|
||||
],
|
||||
),
|
||||
isDisabled: k.boolean(
|
||||
label: 'disabled',
|
||||
initial: false,
|
||||
),
|
||||
isTouched: k.boolean(
|
||||
label: 'touched',
|
||||
initial: false,
|
||||
),
|
||||
isError: k.boolean(
|
||||
label: 'error',
|
||||
initial: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
Story.simple(
|
||||
name: 'Simple Expanded Elevated Button',
|
||||
section: 'Buttons',
|
||||
|
@ -27,7 +75,7 @@ List<Story> getStories(GolekThemeData theme) {
|
|||
? () {}
|
||||
: null,
|
||||
icon: Icon(
|
||||
k.options(
|
||||
k.options<IconData>(
|
||||
label: 'icon',
|
||||
initial: Icons.home,
|
||||
options: const [
|
||||
|
@ -58,247 +106,5 @@ List<Story> getStories(GolekThemeData theme) {
|
|||
),
|
||||
),
|
||||
),
|
||||
Story(
|
||||
name: 'InProgress Text Button',
|
||||
section: 'Buttons',
|
||||
builder: (_, k) => InProgressTextButton(
|
||||
label: k.text(
|
||||
label: 'label',
|
||||
initial: 'Processing',
|
||||
),
|
||||
),
|
||||
),
|
||||
Story(
|
||||
name: 'Favorite Button',
|
||||
section: 'Buttons',
|
||||
builder: (_, k) => FavoriteIconButton(
|
||||
onTap: () {},
|
||||
isFavorite: k.boolean(
|
||||
label: 'isFavorite',
|
||||
initial: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
Story.simple(
|
||||
name: 'Share Icon Button',
|
||||
section: 'Buttons',
|
||||
child: ShareIconButton(onTap: () {}),
|
||||
),
|
||||
Story(
|
||||
name: 'Count Indicator Icon Button',
|
||||
section: 'Count Indicator Buttons',
|
||||
builder: (_, k) => CountIndicatorIconButton(
|
||||
count: k.sliderInt(
|
||||
label: 'count',
|
||||
),
|
||||
iconData: k.options(
|
||||
label: 'iconData',
|
||||
initial: Icons.arrow_upward,
|
||||
options: const [
|
||||
Option(
|
||||
'Upward',
|
||||
Icons.arrow_upward,
|
||||
),
|
||||
Option(
|
||||
'Downward',
|
||||
Icons.arrow_downward,
|
||||
),
|
||||
],
|
||||
),
|
||||
tooltip: k.text(
|
||||
label: 'tooltip',
|
||||
initial: 'Count indicator',
|
||||
),
|
||||
),
|
||||
),
|
||||
Story(
|
||||
name: 'Exception Indicator',
|
||||
section: 'Indicators',
|
||||
builder: (_, k) => ExceptionIndicator(
|
||||
title: k.text(
|
||||
label: 'title',
|
||||
initial: 'Exception title',
|
||||
),
|
||||
message: k.text(
|
||||
label: 'message',
|
||||
initial: 'Exception message',
|
||||
),
|
||||
onTryAgain: k.boolean(
|
||||
label: 'onTryAgain',
|
||||
initial: false,
|
||||
)
|
||||
? () {}
|
||||
: null,
|
||||
),
|
||||
),
|
||||
Story.simple(
|
||||
name: 'Centered Circular Progress Indicator',
|
||||
child: const CenteredCircularProgressIndicator(),
|
||||
),
|
||||
Story(
|
||||
name: 'Rounded Choice Chip',
|
||||
padding: const EdgeInsets.all(
|
||||
Spacing.medium,
|
||||
),
|
||||
builder: (_, k) => RoundedChoiceChip(
|
||||
label: k.text(
|
||||
label: 'label',
|
||||
initial: 'I am a Chip!',
|
||||
),
|
||||
isSelected: k.boolean(
|
||||
label: 'isSelected',
|
||||
initial: false,
|
||||
),
|
||||
avatar: k.boolean(
|
||||
label: 'avatar',
|
||||
initial: false,
|
||||
)
|
||||
? Icon(
|
||||
Icons.favorite,
|
||||
color: theme.warningMainColor,
|
||||
)
|
||||
: null,
|
||||
onSelected: k.boolean(
|
||||
label: 'onSelected',
|
||||
initial: true,
|
||||
)
|
||||
? (_) {}
|
||||
: null,
|
||||
backgroundColor: k.options(
|
||||
label: 'backgroundColor',
|
||||
initial: null,
|
||||
options: const [
|
||||
Option(
|
||||
'Light blue',
|
||||
Colors.lightBlue,
|
||||
),
|
||||
Option(
|
||||
'Red accent',
|
||||
Colors.redAccent,
|
||||
),
|
||||
],
|
||||
),
|
||||
selectedBackgroundColor: k.options(
|
||||
label: 'selectedBackgroundColor',
|
||||
initial: null,
|
||||
options: const [
|
||||
Option(
|
||||
'Green',
|
||||
Colors.green,
|
||||
),
|
||||
Option(
|
||||
'Amber accent',
|
||||
Colors.amberAccent,
|
||||
),
|
||||
],
|
||||
),
|
||||
labelColor: k.options(
|
||||
label: 'labelColor',
|
||||
initial: null,
|
||||
options: const [
|
||||
Option(
|
||||
'Teal',
|
||||
Colors.teal,
|
||||
),
|
||||
Option(
|
||||
'Orange accent',
|
||||
Colors.orangeAccent,
|
||||
),
|
||||
],
|
||||
),
|
||||
selectedLabelColor: k.options(
|
||||
label: 'selectedLabelColor',
|
||||
initial: null,
|
||||
options: const [
|
||||
Option(
|
||||
'Deep purple accent',
|
||||
Colors.deepPurpleAccent,
|
||||
),
|
||||
Option(
|
||||
'Amber accent',
|
||||
Colors.amberAccent,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Story(
|
||||
name: 'Chevron List Tile',
|
||||
padding: const EdgeInsets.all(
|
||||
Spacing.medium,
|
||||
),
|
||||
builder: (_, k) => ChevronListTile(
|
||||
label: k.text(
|
||||
label: 'label',
|
||||
initial: 'Update Profile',
|
||||
),
|
||||
),
|
||||
),
|
||||
Story.simple(
|
||||
name: 'Search Bar',
|
||||
child: const SearchBar(),
|
||||
),
|
||||
Story(
|
||||
name: 'Shrinkable Text',
|
||||
builder: (_, k) => SafeArea(
|
||||
child: ShrinkableText(
|
||||
k.text(
|
||||
label: 'text',
|
||||
initial:
|
||||
'I am shrinkable text. I can resize myself automatically within a space.',
|
||||
),
|
||||
style: k.options(
|
||||
label: 'style',
|
||||
initial: theme.textStyle.copyWith(
|
||||
fontSize: FontSize.style24,
|
||||
),
|
||||
options: [
|
||||
Option(
|
||||
'XX large',
|
||||
theme.textStyle.copyWith(
|
||||
fontSize: FontSize.style24,
|
||||
),
|
||||
),
|
||||
Option(
|
||||
'Small',
|
||||
theme.textStyle.copyWith(
|
||||
fontSize: FontSize.style12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
textAlign: k.options(
|
||||
label: 'textAlign',
|
||||
initial: null,
|
||||
options: const [
|
||||
Option(
|
||||
'Start',
|
||||
TextAlign.start,
|
||||
),
|
||||
Option(
|
||||
'End',
|
||||
TextAlign.end,
|
||||
),
|
||||
Option(
|
||||
'Center',
|
||||
TextAlign.center,
|
||||
),
|
||||
Option(
|
||||
'Justify',
|
||||
TextAlign.justify,
|
||||
),
|
||||
Option(
|
||||
'Left',
|
||||
TextAlign.left,
|
||||
),
|
||||
Option(
|
||||
'Right',
|
||||
TextAlign.right,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility in the flutter_test package. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// gestures. You can also use WidgetTester to find child landing_page in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
|
|
@ -1,21 +1,9 @@
|
|||
export 'src/chevron_list_tile.dart';
|
||||
export 'src/count_indicator_icon_button.dart';
|
||||
export 'src/downvote_icon_button.dart';
|
||||
export 'src/exception_indicator.dart';
|
||||
export 'src/expanded_elevated_button.dart';
|
||||
export 'src/in_progress_text_button.dart';
|
||||
export 'src/favorite_icon_button.dart';
|
||||
export 'src/l10n/component_library_localizations.dart';
|
||||
export 'src/centered_circular_progress_indicator.dart';
|
||||
export 'src/rounded_choice_chip.dart';
|
||||
export 'src/generic_error_snack_bar.dart';
|
||||
export 'src/authentication_required_error_snack_bar.dart';
|
||||
export 'src/row_app_bar.dart';
|
||||
export 'src/search_bar.dart';
|
||||
export 'src/share_icon_button.dart';
|
||||
export 'src/shrinkable_text.dart';
|
||||
export 'src/styled_status_bar.dart';
|
||||
export 'src/theme/font_size.dart';
|
||||
export 'src/theme/spacing.dart';
|
||||
export 'src/theme/golek_theme.dart';
|
||||
export 'src/theme/golek_theme_data.dart';
|
||||
export 'src/input_field.dart';
|
||||
export 'src/error_message_builder.dart';
|
||||
export 'src/favorite_icon_button.dart';
|
|
@ -1,23 +0,0 @@
|
|||
import 'package:component_library/component_library.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AuthenticationRequiredErrorSnackBar extends SnackBar {
|
||||
const AuthenticationRequiredErrorSnackBar({Key? key})
|
||||
: super(
|
||||
key: key,
|
||||
content: const _AuthenticationRequiredErrorSnackBarMessage(),
|
||||
);
|
||||
}
|
||||
|
||||
class _AuthenticationRequiredErrorSnackBarMessage extends StatelessWidget {
|
||||
const _AuthenticationRequiredErrorSnackBarMessage({Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = ComponentLibraryLocalizations.of(context);
|
||||
return Text(
|
||||
l10n.authenticationRequiredErrorSnackbarMessage,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class CenteredCircularProgressIndicator extends StatelessWidget {
|
||||
const CenteredCircularProgressIndicator({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
import 'package:component_library/src/theme/font_size.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ChevronListTile extends StatelessWidget {
|
||||
const ChevronListTile({
|
||||
required this.label,
|
||||
this.onTap,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final String label;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: FontSize.style18,
|
||||
),
|
||||
),
|
||||
trailing: const Icon(
|
||||
Icons.chevron_right_outlined,
|
||||
),
|
||||
onTap: onTap,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
import 'package:component_library/component_library.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CountIndicatorIconButton extends StatelessWidget {
|
||||
const CountIndicatorIconButton({
|
||||
required this.count,
|
||||
required this.iconData,
|
||||
this.iconColor,
|
||||
this.tooltip,
|
||||
this.onTap,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final int count;
|
||||
final IconData iconData;
|
||||
final Color? iconColor;
|
||||
final String? tooltip;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IconButton(
|
||||
onPressed: onTap,
|
||||
tooltip: tooltip,
|
||||
padding: const EdgeInsets.all(0),
|
||||
icon: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
iconData,
|
||||
color: iconColor,
|
||||
),
|
||||
Text(
|
||||
count.toString(),
|
||||
style: const TextStyle(
|
||||
fontSize: FontSize.style12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
import 'package:component_library/component_library.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DownvoteIconButton extends StatelessWidget {
|
||||
const DownvoteIconButton({
|
||||
required this.count,
|
||||
required this.isDownvoted,
|
||||
this.onTap,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final int count;
|
||||
final VoidCallback? onTap;
|
||||
final bool isDownvoted;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = ComponentLibraryLocalizations.of(context);
|
||||
final theme = GolekTheme.of(context);
|
||||
return CountIndicatorIconButton(
|
||||
onTap: onTap,
|
||||
tooltip: l10n.downvoteIconButtonTooltip,
|
||||
iconData: Icons.arrow_downward_sharp,
|
||||
iconColor:
|
||||
isDownvoted ? theme.warningMainColor : theme.neutral20Color,
|
||||
count: count,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import 'package:component_library/component_library.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ErrorMessageBuilder extends StatelessWidget {
|
||||
const ErrorMessageBuilder({
|
||||
Key? key,
|
||||
required this.inputLabel,
|
||||
required this.messageError,
|
||||
}) : super(key: key);
|
||||
|
||||
/// input label ditampilkan title ketika error
|
||||
final String inputLabel;
|
||||
/// pesan error yang ditampilkan
|
||||
final String messageError;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = GolekTheme.of(context);
|
||||
|
||||
return Text(
|
||||
messageError,
|
||||
style: theme.textStyle.copyWith(
|
||||
fontSize: FontSize.style12,
|
||||
color: theme.dangerMainColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
import 'package:component_library/component_library.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ExceptionIndicator extends StatelessWidget {
|
||||
const ExceptionIndicator({
|
||||
this.title,
|
||||
this.message,
|
||||
this.onTryAgain,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final String? title;
|
||||
final String? message;
|
||||
final VoidCallback? onTryAgain;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = ComponentLibraryLocalizations.of(context);
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 32,
|
||||
horizontal: 16,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.error,
|
||||
size: 48,
|
||||
),
|
||||
const SizedBox(
|
||||
height: Spacing.xxLarge,
|
||||
),
|
||||
Text(
|
||||
title ?? l10n.exceptionIndicatorGenericTitle,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: FontSize.style16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Text(
|
||||
title ?? l10n.exceptionIndicatorGenericMessage,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (onTryAgain != null)
|
||||
const SizedBox(
|
||||
height: Spacing.xxxLarge,
|
||||
),
|
||||
if (onTryAgain != null)
|
||||
ExpandedElevatedButton(
|
||||
onTap: onTryAgain,
|
||||
icon: const Icon(
|
||||
Icons.refresh,
|
||||
),
|
||||
label: l10n.exceptionIndicatorTryAgainButton,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
import 'package:component_library/component_library.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class GenericErrorSnackBar extends SnackBar {
|
||||
const GenericErrorSnackBar({Key? key})
|
||||
: super(
|
||||
key: key,
|
||||
content: const _GenericErrorSnackBarMessage(),
|
||||
);
|
||||
}
|
||||
|
||||
class _GenericErrorSnackBarMessage extends StatelessWidget {
|
||||
const _GenericErrorSnackBarMessage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = ComponentLibraryLocalizations.of(context);
|
||||
return Text(
|
||||
l10n.genericErrorSnackbarMessage,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class InProgressTextButton extends StatelessWidget {
|
||||
const InProgressTextButton({
|
||||
required this.label,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final String label;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextButton.icon(
|
||||
icon: Transform.scale(
|
||||
scale: 0.5,
|
||||
child: const CircularProgressIndicator(),
|
||||
),
|
||||
label: Text(
|
||||
label,
|
||||
),
|
||||
onPressed: null,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:component_library/component_library.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'error_message_builder.dart';
|
||||
|
||||
class InputField<T> extends StatefulWidget {
|
||||
final String? label;
|
||||
final String placeholder;
|
||||
final Widget? suffixIcon;
|
||||
final Widget? suffix;
|
||||
final Widget? prefix;
|
||||
final Widget? prefixIcon;
|
||||
final String? errorMessage;
|
||||
final bool obscureText;
|
||||
final TextInputType textInputType;
|
||||
final List<TextInputFormatter>? inputFormatters;
|
||||
final bool isDisabled;
|
||||
final bool isTouched;
|
||||
final bool isError;
|
||||
|
||||
const InputField({
|
||||
Key? key,
|
||||
required this.placeholder,
|
||||
this.label,
|
||||
this.suffixIcon,
|
||||
this.suffix,
|
||||
this.prefix,
|
||||
this.prefixIcon,
|
||||
this.obscureText = false,
|
||||
this.textInputType = TextInputType.text,
|
||||
this.inputFormatters = const [],
|
||||
this.errorMessage,
|
||||
required this.isDisabled,
|
||||
required this.isTouched,
|
||||
required this.isError,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<InputField> createState() => _InputFieldState();
|
||||
}
|
||||
|
||||
class _InputFieldState extends State<InputField> {
|
||||
bool _hasFocus = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = GolekTheme.of(context);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (widget.label != null && widget.label?.isEmpty == true) ...[
|
||||
Text(
|
||||
widget.label!,
|
||||
style: theme.textStyle.copyWith(fontSize: FontSize.style14),
|
||||
),
|
||||
],
|
||||
Focus(
|
||||
onFocusChange: (value) {
|
||||
setState(() => _hasFocus = value);
|
||||
},
|
||||
child: Container(
|
||||
height: 52,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color:
|
||||
_hasFocus ? theme.successBorderColor : Colors.transparent,
|
||||
width: 3,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
child: TextField(
|
||||
inputFormatters: widget.inputFormatters,
|
||||
keyboardType: widget.textInputType,
|
||||
obscureText: widget.obscureText,
|
||||
selectionHeightStyle: BoxHeightStyle.strut,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 9.5,
|
||||
vertical: 10,
|
||||
),
|
||||
counterText: '',
|
||||
fillColor: widget.isDisabled
|
||||
? const Color(0xffededed)
|
||||
: Colors.transparent,
|
||||
filled: true,
|
||||
prefix: widget.prefix,
|
||||
prefixIcon: widget.prefixIcon,
|
||||
suffix: widget.suffix,
|
||||
suffixIcon: widget.suffixIcon,
|
||||
errorStyle: theme.textStyle.copyWith(
|
||||
fontSize: 0,
|
||||
),
|
||||
prefixIconConstraints: const BoxConstraints(
|
||||
minHeight: 24,
|
||||
minWidth: 40,
|
||||
),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
borderSide: BorderSide(color: theme.primaryBorderColor),
|
||||
),
|
||||
label: Text(
|
||||
widget.placeholder,
|
||||
style: theme.textStyle.copyWith(
|
||||
fontSize: FontSize.style14,
|
||||
color: widget.isTouched && widget.isError
|
||||
? theme.dangerMainColor
|
||||
: theme.neutral70Color,
|
||||
),
|
||||
),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
),
|
||||
style: theme.textStyle.copyWith(fontSize: FontSize.style14),
|
||||
),
|
||||
),
|
||||
),
|
||||
ErrorMessageBuilder(
|
||||
inputLabel: widget.label ?? '',
|
||||
messageError: widget.errorMessage ?? '',
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
import 'package:component_library/component_library.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class RoundedChoiceChip extends StatelessWidget {
|
||||
const RoundedChoiceChip({
|
||||
required this.label,
|
||||
required this.isSelected,
|
||||
this.avatar,
|
||||
this.labelColor,
|
||||
this.selectedLabelColor,
|
||||
this.backgroundColor,
|
||||
this.selectedBackgroundColor,
|
||||
this.onSelected,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final String label;
|
||||
final Widget? avatar;
|
||||
final ValueChanged<bool>? onSelected;
|
||||
final Color? labelColor;
|
||||
final Color? selectedLabelColor;
|
||||
final Color? backgroundColor;
|
||||
final Color? selectedBackgroundColor;
|
||||
final bool isSelected;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = GolekTheme.of(context);
|
||||
return ChoiceChip(
|
||||
shape: const StadiumBorder(
|
||||
side: BorderSide(),
|
||||
),
|
||||
avatar: avatar,
|
||||
label: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: isSelected
|
||||
? (selectedLabelColor ?? theme.primaryMainColor)
|
||||
: (labelColor ?? theme.primarySurfaceColor),
|
||||
),
|
||||
),
|
||||
onSelected: onSelected,
|
||||
selected: isSelected,
|
||||
backgroundColor:
|
||||
(backgroundColor ?? theme.primaryMainColor),
|
||||
selectedColor: (selectedBackgroundColor ??
|
||||
theme.primarySurfaceColor),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
import 'package:component_library/component_library.dart';
|
||||
import 'package:component_library/src/theme/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class RowAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
const RowAppBar({
|
||||
Key? key,
|
||||
this.children = const [],
|
||||
}) : super(key: key);
|
||||
|
||||
final List<Widget> children;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: Spacing.small,
|
||||
left: Spacing.small,
|
||||
right: Spacing.small,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const BackButton(),
|
||||
...children,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
import 'package:component_library/component_library.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SearchBar extends StatelessWidget {
|
||||
const SearchBar({
|
||||
this.controller,
|
||||
this.onChanged,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final TextEditingController? controller;
|
||||
final ValueChanged<String>? onChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = ComponentLibraryLocalizations.of(context);
|
||||
return TextField(
|
||||
controller: controller,
|
||||
decoration: InputDecoration(
|
||||
suffixIcon: const Icon(
|
||||
Icons.search,
|
||||
),
|
||||
hintText: l10n.searchBarHintText,
|
||||
labelText: l10n.searchBarLabelText,
|
||||
),
|
||||
onChanged: onChanged,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
import 'package:component_library/component_library.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ShareIconButton extends StatelessWidget {
|
||||
const ShareIconButton({
|
||||
Key? key,
|
||||
this.onTap,
|
||||
}) : super(key: key);
|
||||
|
||||
final VoidCallback? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = ComponentLibraryLocalizations.of(context);
|
||||
return IconButton(
|
||||
onPressed: onTap,
|
||||
tooltip: l10n.shareIconButtonTooltip,
|
||||
icon: const Icon(
|
||||
Icons.share,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ShrinkableText extends StatelessWidget {
|
||||
const ShrinkableText(
|
||||
this.data, {
|
||||
this.style,
|
||||
this.textAlign = TextAlign.center,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final String data;
|
||||
final TextStyle? style;
|
||||
final TextAlign? textAlign;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AutoSizeText(
|
||||
data,
|
||||
style: style,
|
||||
textAlign: textAlign,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
// Useful for changing the status bar color when the screen doesn't have an
|
||||
// AppBar. For screens with AppBars, please use the [] property.
|
||||
class StyledStatusBar extends StatelessWidget {
|
||||
const StyledStatusBar._({
|
||||
required this.child,
|
||||
required this.style,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
const StyledStatusBar.light({
|
||||
required Widget child,
|
||||
Key? key,
|
||||
}) : this._(
|
||||
child: child,
|
||||
style: SystemUiOverlayStyle.light,
|
||||
key: key,
|
||||
);
|
||||
|
||||
const StyledStatusBar.dark({
|
||||
required Widget child,
|
||||
Key? key,
|
||||
}) : this._(
|
||||
child: child,
|
||||
style: SystemUiOverlayStyle.dark,
|
||||
key: key,
|
||||
);
|
||||
|
||||
final Widget child;
|
||||
final SystemUiOverlayStyle style;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: style,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -117,6 +117,60 @@ class LightGolekThemeData extends GolekThemeData {
|
|||
brightness: Brightness.light,
|
||||
primarySwatch: Colors.black.toMaterialColor(),
|
||||
dividerTheme: _dividerThemeData,
|
||||
backgroundColor: neutral10Color,
|
||||
appBarTheme: const AppBarTheme(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
),
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: primaryMainColor,
|
||||
side: const BorderSide(
|
||||
color: Colors.transparent,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
),
|
||||
),
|
||||
primaryColor: primaryMainColor,
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
errorStyle: const TextStyle(fontSize: 0, height: 1),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
borderSide: BorderSide(color: neutral50Color),
|
||||
),
|
||||
disabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
borderSide: BorderSide(color: neutral50Color),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
borderSide: BorderSide(color: neutral50Color),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
borderSide: BorderSide(
|
||||
color: primaryBorderColor,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
hintStyle: textStyle.copyWith(
|
||||
fontSize: FontSize.style14,
|
||||
color: neutral70Color,
|
||||
),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
),
|
||||
colorScheme: ColorScheme.light(
|
||||
primary: primaryMainColor, // header background color
|
||||
onSurface: secondaryMainColor, // body text color
|
||||
),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: primaryMainColor, // button text color
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
|
@ -219,13 +273,13 @@ class LightGolekThemeData extends GolekThemeData {
|
|||
Color get successPressedColor => const Color(0xff18D48C);
|
||||
|
||||
@override
|
||||
Color get successSurfaceColor => const Color(0xffECFFF8);
|
||||
Color get successSurfaceColor => const Color(0xffECFFF8);
|
||||
|
||||
@override
|
||||
Color get tertiaryBorderColor => const Color(0xffDCE6F6);
|
||||
Color get tertiaryBorderColor => const Color(0xffDCE6F6);
|
||||
|
||||
@override
|
||||
Color get tertiaryFocusColor => const Color.fromRGBO(198, 207, 221, 0.2);
|
||||
Color get tertiaryFocusColor => const Color.fromRGBO(198, 207, 221, 0.2);
|
||||
|
||||
@override
|
||||
Color get tertiaryHoverColor => const Color(0xffB0B8C5);
|
||||
|
@ -237,22 +291,22 @@ class LightGolekThemeData extends GolekThemeData {
|
|||
Color get tertiaryPressedColor => const Color(0xff9AA1AC);
|
||||
|
||||
@override
|
||||
Color get tertiarySurfaceColor =>const Color(0xffF5F5F5);
|
||||
Color get tertiarySurfaceColor => const Color(0xffF5F5F5);
|
||||
|
||||
@override
|
||||
Color get warningBorderColor => const Color(0xffFFF9CF);
|
||||
|
||||
@override
|
||||
Color get warningHoverColor => const Color(0xffFEE31A);
|
||||
Color get warningHoverColor => const Color(0xffFEE31A);
|
||||
|
||||
@override
|
||||
Color get warningMainColor => const Color(0xffFFEE6E);
|
||||
|
||||
@override
|
||||
Color get warningPressedColor => const Color(0xffCBB511);
|
||||
Color get warningPressedColor => const Color(0xffCBB511);
|
||||
|
||||
@override
|
||||
Color get warningSurfaceColor => const Color(0xffFFFCE2);
|
||||
Color get warningSurfaceColor => const Color(0xffFFFCE2);
|
||||
}
|
||||
|
||||
class DarkGolekThemeData extends GolekThemeData {
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 90 KiB |
Binary file not shown.
After Width: | Height: | Size: 61 KiB |
|
@ -1,7 +1,6 @@
|
|||
import 'package:component_library/component_library.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:landing_menu/src/widgets/landing_app_bar.dart';
|
||||
import 'landing_page/landing_app_bar.dart';
|
||||
|
||||
class LandingPage extends StatefulWidget {
|
||||
const LandingPage({Key? key}) : super(key: key);
|
||||
|
@ -57,7 +56,8 @@ class _LandingPageState extends State<LandingPage> {
|
|||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/truckershipper.png',
|
||||
'assets/trucker_shipper.png',
|
||||
package: 'features/landing_menu',
|
||||
width: 300,
|
||||
height: 200,
|
||||
),
|
||||
|
@ -94,7 +94,7 @@ class _LandingPageState extends State<LandingPage> {
|
|||
onClickInfo: () => {},
|
||||
label: 'Cari Truk',
|
||||
description: 'Pengirim Muatan',
|
||||
assets: 'assets/images/box_shipper.png',
|
||||
assets: 'assets/box_shipper.png',
|
||||
onTap: () {},
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
|
@ -102,7 +102,7 @@ class _LandingPageState extends State<LandingPage> {
|
|||
onClickInfo: () => {},
|
||||
label: 'Cari Muatan',
|
||||
description: 'Penyedia Angkutan',
|
||||
assets: 'assets/images/truk_trucker.png',
|
||||
assets: 'assets/truk_trucker.png',
|
||||
onTap: () async {},
|
||||
),
|
||||
],
|
||||
|
@ -173,7 +173,7 @@ class CardProduct extends StatelessWidget {
|
|||
),
|
||||
const SizedBox(height: 8),
|
||||
Image(
|
||||
image: AssetImage(assets),
|
||||
image: AssetImage(assets, package: 'features/landing_menu'),
|
||||
width: 131,
|
||||
height: 100,
|
||||
fit: BoxFit.contain,
|
||||
|
|
|
@ -11,6 +11,7 @@ class LandingAppBar extends StatelessWidget with PreferredSizeWidget {
|
|||
final theme = GolekTheme.of(context);
|
||||
|
||||
return AppBar(
|
||||
elevation: 0,
|
||||
backgroundColor: theme.primaryMainColor,
|
||||
actions: [
|
||||
CompositedTransformTarget(
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility that Flutter provides. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// gestures. You can also use WidgetTester to find child landing_page in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility that Flutter provides. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// gestures. You can also use WidgetTester to find child landing_page in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
|
|
@ -34,3 +34,5 @@ dev_dependencies:
|
|||
build_runner: ^2.3.2
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
assets:
|
||||
- packages/features/landing_menu/assets/
|
Loading…
Reference in New Issue