YamamotoDesu / flutter_build_production_app

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

flutter_production_app

Reusable Component

Reusable Widget - Scaffold

lib/common/widget/app_scaffold.dart

import 'package:flutter/material.dart';

class AppScaffold extends StatelessWidget {
  final Widget title;
  final Widget widget;
  final bool centerTitle;
  final ScrollController? controller;

  const AppScaffold({
    Key? key,
    required this.title,
    required this.widget,
    this.centerTitle = false,
    this.controller,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: title,
        centerTitle: centerTitle,
        elevation: 0,
      ),
      body: SafeArea(
        child: SingleChildScrollView(
          controller: controller,
          child: widget,
        ),
      ),
    );
  }
}

lib/common/widget/app_scaffold_slive.dart

import 'package:flutter/material.dart';

class AppScaffoldSliver extends StatelessWidget {
  final Widget title;
  final List<Widget> slivers;
  final bool centerTitle;
  final ScrollController? controller;

  const AppScaffoldSliver({
    Key? key,
    required this.title,
    required this.slivers,
    this.centerTitle = false,
    this.controller,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: title,
        centerTitle: centerTitle,
        elevation: 0,
      ),
      body: SafeArea(
        child: CustomScrollView(
          controller: controller,
          slivers: slivers,
        ),
      ),
    );
  }
}

Reusable Widget - TextField

lib/common/widget/form/custom_text_form_field.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class CustomTextFormField extends StatefulWidget {
  const CustomTextFormField({
    Key? key,
    ValueKey? textFieldKey,
    required String labelText,
    String? hintText,
    Widget? prefixIcon,
    Widget? suffixIcon,
    bool isObscureText = false,
    required TextInputType keyboardType,
    required TextInputAction textInputAction,
    List<TextInputFormatter>? inputFormatters,
    String? Function(String?)? validator,
    String? Function(String?)? onChanged,
    required TextEditingController controller,
  })  : _textFieldKey = textFieldKey,
        _labelText = labelText,
        _hintText = hintText,
        _prefixIcon = prefixIcon,
        _suffixIcon = suffixIcon,
        _validator = validator,
        _isObscureText = isObscureText,
        _controller = controller,
        _onChanged = onChanged,
        _keyboardType = keyboardType,
        _textInputAction = textInputAction,
        _inputFormatters = inputFormatters,
        super(key: key);

  final ValueKey? _textFieldKey;
  final TextEditingController _controller;
  final String _labelText;
  final String? _hintText;
  final Widget? _prefixIcon;
  final Widget? _suffixIcon;
  final bool _isObscureText;
  final TextInputType _keyboardType;
  final TextInputAction _textInputAction;
  final List<TextInputFormatter>? _inputFormatters;
  final String? Function(String?)? _validator;
  final String? Function(String?)? _onChanged;

  @override
  _CustomTextFormFieldStateState createState() =>
      _CustomTextFormFieldStateState();
}

class _CustomTextFormFieldStateState extends State<CustomTextFormField> {
  String _value = "";

  @override
  void initState() {
    super.initState();
    widget._controller.addListener(() => setTextValue());
  }

  @override
  void dispose() {
    widget._controller.removeListener(() => setTextValue());
    super.dispose();
  }

  void setTextValue() {
    setState(() {
      _value = widget._controller.text;
    });
  }

  @override
  Widget build(BuildContext context) {
    return TextFormField(
      key: widget._textFieldKey,
      controller: widget._controller,
      obscureText: widget._isObscureText,
      keyboardType: widget._keyboardType,
      textInputAction: widget._textInputAction,
      decoration: InputDecoration(
        labelText: widget._labelText,
        hintText: widget._hintText,
        floatingLabelBehavior: FloatingLabelBehavior.always,
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(8),
        ),
        prefixIcon: widget._prefixIcon,
        suffixIcon: _value.isNotEmpty ? widget._suffixIcon : null,
      ),
      inputFormatters: widget._inputFormatters,
      validator: widget._validator,
      onChanged: widget._onChanged,
    );
  }
}

Reusable Widget - Primary Button

lib/common/widget/button/primary_button.dart

import 'package:flutter/material.dart';

class PrimaryButton extends StatelessWidget {
  final String text;
  final bool isLoading;
  final bool isEnabled;
  final VoidCallback? onPressed;

  const PrimaryButton({
    Key? key,
    required this.text,
    this.isLoading = false,
    this.isEnabled = false,
    this.onPressed,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      children: [
        Flexible(
          flex: 1,
          fit: FlexFit.tight,
          child: FilledButton.tonal(
            style: FilledButton.styleFrom(
              padding: const EdgeInsets.all(8),
            ),
            onPressed: isEnabled ? onPressed : null,
            child: isLoading
                ? const CircularProgressIndicator.adaptive()
                : Text(
                    text,
                    textAlign: TextAlign.center,
                    style: Theme.of(context).textTheme.headlineSmall,
                  ),
          ),
        )
      ],
    );
  }
}

Reusable Widget - Widget Keys

lib/common/widget/widget_key.dart

import 'package:flutter/material.dart';

const nameTextKey = ValueKey('name');
const emailTextKey = ValueKey('email');
const passwordTextKey = ValueKey('password');
const confirmPasswordTextKey = ValueKey('confirmPassword');
const phoneTextKey = ValueKey('phone');
const btnLoginKey = ValueKey('btnLogin');
const btnRegisterKey = ValueKey('btnRegister');
const btnLogoutKey = ValueKey('btnLogout');
const btnYesKey = ValueKey('btnYes');
const btnNoKey = ValueKey('btnNo');
const registerNowTextKey = ValueKey('registerNow');

Reusable Widget - Dimens

lib/common/styles/dimens.dart

const double kXXXSmall = 1.0;
const double kXXSmall = 2.0;
const double kXSmall = 4.0;
const double kSmall = 8.0;
const double kSMedium = 12.0;
const double kMedium = 16.0;
const double kXLMedium = 20.0;
const double kLarge = 24.0;
const double kXLarge = 32.0;
const double kXXLarge = 40.0;
const double kXXXLarge = 48.0;
const double kXXXXLarge = 64.0;
const double kLeadingWidth = 96.0;

Reusable Widget - Dialog

lib/common/widget/dialog/confirm_dialog.dart

import 'package:flutter/material.dart';
import 'package:flutter_setup/common/styles/dimens.dart';
import 'package:flutter_setup/common/widget/widget_key.dart';

mixin ConfirmDialog {
  Future<void> showConfirmDialog({
    required BuildContext context,
    required String title,
    required String msg,
    required String btnYesText,
    required String btnNoText,
    bool barrierDismissible = true,
    required VoidCallback onYesTap,
    required VoidCallback onNoTap,
  }) {
    final textTheme = Theme.of(context).textTheme;

    return showDialog(
      context: context,
      barrierDismissible: barrierDismissible,
      builder: (context) {
        return WillPopScope(
          onWillPop: () async => barrierDismissible,
          child: AlertDialog(
            clipBehavior: Clip.antiAlias,
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(kSmall),
            ),
            content: Container(
              padding: const EdgeInsets.only(
                top: kXLarge,
                bottom: kMedium,
                left: kMedium,
                right: kMedium,
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.center,
                mainAxisSize: MainAxisSize.min,
                children: [
                  const Icon(Icons.info),
                  const SizedBox(height: kMedium),
                  Text(title, style: textTheme.headlineSmall),
                  const SizedBox(height: kMedium),
                  Text(
                    msg,
                    style: textTheme.bodySmall,
                    textAlign: TextAlign.center,
                  ),
                  const SizedBox(height: kLarge),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      ElevatedButton.icon(
                        key: btnNoKey,
                        onPressed: onNoTap,
                        icon: const Icon(Icons.close),
                        label: Text(btnNoText),
                      ),
                      const SizedBox(
                        width: kMedium,
                      ),
                      ElevatedButton.icon(
                        key: btnYesKey,
                        onPressed: onYesTap,
                        icon: const Icon(Icons.check),
                        label: Text(btnYesText),
                      )
                    ],
                  ),
                ],
              ),
            ),
          ),
        );
      },
    );
  }
}

Reusable Widget -

Checkbox lib/common/widget/checkbox/check_box_widget.dart

import 'package:flutter/material.dart';

class CheckboxWidget extends StatefulWidget {
  final String title;
  final String subtitle;
  final bool? value;
  final FormFieldValidator<bool>? validator;
  final Function(bool?) onChanged;

  const CheckboxWidget({
    required this.title,
    required this.subtitle,
    required this.value,
    required this.validator,
    required this.onChanged,
    Key? key,
  }) : super(key: key);

  @override
  State<CheckboxWidget> createState() => _CheckboxWidgetState();
}

class _CheckboxWidgetState extends State<CheckboxWidget> {
  @override
  Widget build(BuildContext context) {
    return FormField(
      validator: widget.validator,
      builder: (field) => Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          CheckboxListTile(
            title: Text(widget.title),
            subtitle: Text(widget.subtitle),
            value: widget.value,
            onChanged: (value) {
              widget.onChanged(value);
              field.didChange(value);
              field.validate();
            },
          ),
          if (field.hasError) ...[
            Text(
              field.errorText!,
              style: Theme.of(context).textTheme.bodySmall?.copyWith(
                    color: Colors.red,
                  ),
            ),
          ],
        ],
      ),
    );
  }
}

Extension - StringHardCoded

lib/common/extentions/string_hardcoded.dart

extension StringHardCoded on String {
  String get toHardCoded => this;
}

Mixin -Input Validater

lib/common/mixin/input_validation_mixin.dart

enum ValidateFailResult {
  empty,
  invalidEmail,
  invalidPassword,
  passwordNotMatch,
  invalidAtLeastEightCharacter,
  invalidPhoneNumber,
  invalidLength,
  invalidPasswordType,
  isNewPasswordMatchOldPassword,
  isNewPwdAndConfirmedPwdNotMatched,
}

typedef Validator = ValidateFailResult? Function(String?);
typedef ValidatorString = String? Function(String?);

mixin InputValidationMixin {
  final hasNumberRegEx = RegExp(r'[0-9]+');
  final hasCharacterRegEx = RegExp(r"[a-zA-Z]+");
  final isNumber = RegExp(r'[^0-9]{1,}');
  final isEmailRegEx = RegExp(
    r"^[a-zA-Z0-9.!#$%&'.*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?).*$",
  );
  final isPhoneNumber = RegExp(
    r'^(016|086|096|097|098|032|033|034|035|036|037|038|039|090|093|0120|0121|0122|0126|0128|0896|091|094|083|084|085|081|082|092|056|058|099|059|0296|0254|0209|0204|0291|0222|0275|0256|0274|0271|0252|0290|0292|0206|0236|0262|0261|0215|0251|0277|0269|0226|024|0239|0220|0225|0293|079|028|0221|0258|0297|0260|0213|0263|0205|0214|0272|0228|0238|0259|0229|0257|0232|0235|0255|0203|0233|0299|0212|0276|0227|0208|0237|0234|0273|0294|0207|0270|0216|08|06|09)([0-9]{6,9})$',
  );

  ValidateFailResult? isTextEmpty(String? value) {
    if (value == null || value.isEmpty) {
      return ValidateFailResult.empty;
    }
    return null;
  }

  ValidateFailResult? isInvalidEmail(String? value) {
    if (value == null || !isEmailRegEx.hasMatch(value)) {
      return ValidateFailResult.invalidEmail;
    }
    return null;
  }

  ValidateFailResult? invalidPasswordType(bool? invalid) {
    if (invalid != null) {
      return ValidateFailResult.invalidPasswordType;
    }
    return null;
  }

  // at least 8 character (a-z)
  // at least 1 number (0-9)
  ValidateFailResult? isPasswordInvalid(String? value) {
    bool isInvalidLength = (value == null || value.length < 6);

    if (isInvalidLength) {
      return ValidateFailResult.invalidLength;
    }

    bool haveAtLeastOneNumber = hasNumberRegEx.hasMatch(value);
    bool haveAtLeastOneCharacter = hasCharacterRegEx.hasMatch(value);

    if (!haveAtLeastOneNumber || !haveAtLeastOneCharacter) {
      return ValidateFailResult.invalidPassword;
    }

    return null;
  }

  ValidateFailResult? isInvalidPhoneNumber(String? value) {
    String? isValidNumber = value?.replaceAll(isNumber, '');

    if (isValidNumber != null) {
      if (isValidNumber.length < 10) {
        return ValidateFailResult.invalidPhoneNumber;
      }
      if (!isPhoneNumber.hasMatch(isValidNumber)) {
        return ValidateFailResult.invalidPhoneNumber;
      }
    }

    return null;
  }

  ValidatorString combine(List<ValidatorString> validators) {
    return (String? str) {
      for (final validator in validators) {
        final result = validator(str);
        if (result != null) {
          return result;
        }
      }
      return null;
    };
  }

  ValidatorString withMessage(String message, Validator validator) {
    return (String? str) {
      final result = validator(str);
      if (result != null) {
        return message;
      }
      return null;
    };
  }

  String? isValidTermsAndConditions(bool? value, String message) {
    if (value == false || value == null) {
      return message;
    }
    return null;
  }
}

Text Theme Context

import 'package:flutter/material.dart';

extension TextThemeContext on BuildContext {
  TextTheme get textTheme => Theme.of(this).textTheme;
}

Async Value Widget

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_setup/core/exception/failure.dart';

class AsyncValueWidget<T> extends StatelessWidget {
  final AsyncValue<T> value;
  final Widget Function(T) data;
  const AsyncValueWidget({
    super.key,
    required this.value,
    required this.data,
  });

  @override
  Widget build(BuildContext context) {
    return value.when(
      data: data,
      error: (error, stack) {
        final failure = error as Failure;
        return Center(
          child: Text(failure.message),
        );
      },
      loading: () => const Center(
        child: CircularProgressIndicator(),
      ),
    );
  }
}

InputPhoneFormatter

pubspec.yaml

  mask_text_input_formatter: ^2.5.0

lib/common/mixin/input_phone_formatter_mixin.dart

import 'package:mask_text_input_formatter/mask_text_input_formatter.dart';

mixin InputPhoneFormatter {
  var maskPhoneFormatter = MaskTextInputFormatter(
    mask: '###-###-####', //016-196-0210
    filter: {"#": RegExp(r'[0-9]')},
    type: MaskAutoCompletionType.lazy,
  );
}

Sign up

image

lib/features/auth/presentation/ui/signup_screen.dart

class SignUpScreen extends ConsumerStatefulWidget {
  const SignUpScreen({super.key});

  @override
  ConsumerState<SignUpScreen> createState() => _SignUpScreenState();
}

class _SignUpScreenState extends BaseConsumerState<SignUpScreen>
    with InputValidationMixin, InputPhoneFormatter {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  final TextEditingController _nameController = TextEditingController();
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();
  final TextEditingController _confirmPasswordController =
      TextEditingController();
  final TextEditingController _phoneController = TextEditingController();

  @override
  void dispose() {
    _nameController.dispose();
    _emailController.dispose();
    _passwordController.dispose();
    _confirmPasswordController.dispose();
    _phoneController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AppScaffold(
      title: const Text('SignUp'),
      widget: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Form(
          key: _formKey,
          child: Column(
            children: [
              const SizedBox(
                height: 16,
              ),
              CustomTextFormField(
                labelText: 'Name'.toHardCoded,
                hintText: 'Enter your name'.toHardCoded,
                keyboardType: TextInputType.text,
                textInputAction: TextInputAction.next,
                controller: _nameController,
                prefixIcon: const Icon(Icons.person),
                suffixIcon: IconButton(
                  icon: const Icon(Icons.clear),
                  onPressed: () {
                    _nameController.clear();
                  },
                ),
                validator: combine([
                  withMessage(
                    'Name is required'.toHardCoded,
                    isTextEmpty,
                  ),
                ]),
                onChanged: (String? value) {},
              ),
              const SizedBox(
                height: 8,
              ),
              CustomTextFormField(
                labelText: 'Email'.toHardCoded,
                hintText: 'Enter your email'.toHardCoded,
                keyboardType: TextInputType.emailAddress,
                textInputAction: TextInputAction.next,
                controller: _emailController,
                prefixIcon: const Icon(Icons.email),
                suffixIcon: IconButton(
                  icon: const Icon(Icons.clear),
                  onPressed: () {
                    _emailController.clear();
                  },
                ),
                validator: combine([
                  withMessage(
                    'email is required'.toHardCoded,
                    isTextEmpty,
                  ),
                  withMessage(
                    'email is invalid'.toHardCoded,
                    isInvalidEmail,
                  ),
                ]),
                onChanged: (String? value) {},
              ),
              const SizedBox(
                height: 8,
              ),
              CustomTextFormField(
                labelText: 'Password'.toHardCoded,
                hintText: 'Enter your password'.toHardCoded,
                keyboardType: TextInputType.visiblePassword,
                textInputAction: TextInputAction.next,
                controller: _passwordController,
                isObscureText: true,
                prefixIcon: const Icon(Icons.visibility_off),
                suffixIcon: IconButton(
                  icon: const Icon(Icons.clear),
                  onPressed: () {
                    _passwordController.clear();
                  },
                ),
                validator: combine([
                  withMessage(
                    'password is required'.toHardCoded,
                    isTextEmpty,
                  ),
                  withMessage(
                    'password is invalid'.toHardCoded,
                    isPasswordInvalid,
                  ),
                ]),
                onChanged: (String? value) {},
              ),
              const SizedBox(
                height: 8,
              ),
              CustomTextFormField(
                labelText: 'Confirm Password'.toHardCoded,
                hintText: 'Enter your confirm password'.toHardCoded,
                keyboardType: TextInputType.visiblePassword,
                textInputAction: TextInputAction.next,
                controller: _confirmPasswordController,
                isObscureText: true,
                prefixIcon: const Icon(Icons.visibility_off),
                suffixIcon: IconButton(
                  icon: const Icon(Icons.clear),
                  onPressed: () {
                    _confirmPasswordController.clear();
                  },
                ),
                validator: combine([
                  withMessage(
                    'password is required'.toHardCoded,
                    isTextEmpty,
                  ),
                  withMessage(
                    'password is invalid'.toHardCoded,
                    isPasswordInvalid,
                  ),
                ]),
                onChanged: (String? value) {},
              ),
              const SizedBox(
                height: 8,
              ),
              CustomTextFormField(
                labelText: 'Phone'.toHardCoded,
                hintText: 'Enter your phone password'.toHardCoded,
                keyboardType: TextInputType.phone,
                textInputAction: TextInputAction.next,
                controller: _phoneController,
                prefixIcon: const Icon(Icons.visibility_off),
                suffixIcon: IconButton(
                  icon: const Icon(Icons.clear),
                  onPressed: () {
                    _phoneController.clear();
                  },
                ),
                inputFormatters: [maskPhoneFormatter],
                validator: combine([
                  withMessage(
                    'phone is required'.toHardCoded,
                    isTextEmpty,
                  ),
                  withMessage(
                    'phone is invalid'.toHardCoded,
                    isInvalidPhoneNumber,
                  ),
                ]),
                onChanged: (String? value) {},
              ),
              const SizedBox(
                height: 8,
              ),
              CheckboxWidget(
                title: 'Terms and conditions',
                subtitle: 'Please accept the terms and conditions',
                value: false,
                validator: (value) {
                  return isValidTermsAndConditions(
                    value,
                    'Please accept the terms and conditions',
                  );
                },
                onChanged: (value) {},
              ),
              const SizedBox(
                height: 8,
              ),
              PrimaryButton(
                text: 'Sign Up',
                isEnabled: true,
                isLoading: false,
                onPressed: () {},
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Signup API service

lib/features/auth/signup/data/api/sign_up_api_service.dart

import 'package:dio/dio.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_setup/core/remote/network_service.dart';
import 'package:flutter_setup/features/auth/signup/data/dto/sign_up_response.dart';
import 'package:retrofit/retrofit.dart';

part 'sign_up_api_service.g.dart';

/// provider for SignUpApiService
final signUpApiServiceProvider = Provider<SignUpApiService>((ref) {
  final dio = ref.watch(networkServiceProvider);

  return SignUpApiService(dio);
});

@RestApi()
abstract class SignUpApiService {
  factory SignUpApiService(Dio dio) => _SignUpApiService(dio);

  @POST('api/v1/register')
  Future<SignUpResponse> signUp(@Body() Map<String, dynamic> request);
}

lib/features/auth/signup/data/dto/sign_up_response.dart

@freezed
class SignUpResponse with _$SignUpResponse {
  const factory SignUpResponse({
    required String id,
    required String name,
    required String email,
    required String phone,
    @JsonKey(name: 'updated_at') required DateTime updatedAt,
    @JsonKey(name: 'created_at') required DateTime createdAt,
  }) = _SignUpResponse;

  factory SignUpResponse.fromJson(Map<String, dynamic> json) =>
      _$SignUpResponseFromJson(json);
}

Signup API Repository

lib/features/auth/signup/data/repository/isign_up_repository.dart

import 'package:flutter_setup/features/auth/signup/data/dto/sign_up_response.dart';

abstract class ISignUpRepository {
  Future<SignUpResponse> signUp(Map<String, dynamic> request);
}

lib/features/auth/signup/data/repository/sign_up_repository.dart

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_setup/features/auth/signup/data/api/sign_up_api_service.dart';
import 'package:flutter_setup/features/auth/signup/data/dto/sign_up_response.dart';
import 'package:flutter_setup/features/auth/signup/data/repository/isign_up_repository.dart';

/// provider for ISignUpRepository
final signUpRepositoryProvider = Provider<ISignUpRepository>((ref) {
  final _signUpApiService = ref.watch(signUpApiServiceProvider);
  return SignUpRepository(_signUpApiService);
});

class SignUpRepository implements ISignUpRepository {
  final SignUpApiService _signUpApiService;

  SignUpRepository(this._signUpApiService);

  @override
  Future<SignUpResponse> signUp(Map<String, dynamic> request) async {
    return await _signUpApiService.signUp(request);
  }
}

SignUp Service

lib/features/auth/signup/application/isign_up_service.dart

import 'package:flutter_setup/features/auth/signup/data/dto/sign_up_response.dart';

abstract class ISignUpService {
  Future<SignUpResponse> signUp(Map<String, dynamic> request);
}

lib/features/auth/signup/application/sign_up_service.dart

/// provider for SignUpApiService
final signUpApiServiceProvider = Provider<ISignUpService>((ref) {
  final signUpRepository = ref.watch(signUpRepositoryProvider);
  return SignUpApiService(signUpRepository);
});

class SignUpApiService implements ISignUpService {
  final ISignUpRepository _signUpRepository;

  SignUpApiService(this._signUpRepository);

  @override
  Future<SignUpResponse> signUp(Map<String, dynamic> request) async {
    return await _signUpRepository.signUp(request);
  }
}

SignUp State

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

part 'sign_up_state.freezed.dart';

@freezed
class SignUpState with _$SignUpState {
  const factory SignUpState({
    @Default(AsyncValue<bool>.data(false)) final AsyncValue<bool> isSignUp,
    @Default(false) final bool isLoading,
    @Default(true) final bool isObscure,
    @Default(false) final bool isTermsAndCondition,
    @Default([]) final Map<String, dynamic> formData,
  }) = _SignUpState;
}

SignUp Controller

lib/features/auth/signup/presentation/controller/sign_up_controller.dart

// ignore_for_file: prefer_const_constructors

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_setup/features/auth/signup/application/sign_up_service.dart';
import 'package:flutter_setup/features/auth/signup/presentation/state/sign_up_state.dart';

final signUpControllerProvider =
    NotifierProvider<SignUpController, SignUpState>(SignUpController.new);

class SignUpController extends Notifier<SignUpState> {
  @override
  SignUpState build() {
    return SignUpState();
  }

  void signUp() async {
    final signUpService = ref.read(signUpApiServiceProvider);
    state = state.copyWith(isLoading: true);
    final result = await signUpService.signUp(state.formData);
    if (result.id > 0) {
      state = state.copyWith(
        isLoading: false,
        isSignUp: const AsyncValue.data(true),
      );
    }
  }

  void setTermAndCondition(bool value) {
    state = state.copyWith(isTermsAndCondition: value);
  }

  void setIsObsure() {
    state = state.copyWith(isObscure: !state.isObscure);
  }

  void setFormData({required String key, required dynamic value}) {
    state = state.copyWith(
      formData: {
        ...state.formData,
        ...{
          key: value,
        },
      },
    );
  }
}

About


Languages

Language:Dart 68.9%Language:C++ 14.5%Language:CMake 11.3%Language:Ruby 1.7%Language:Swift 1.6%Language:HTML 1.1%Language:C 0.9%Language:Kotlin 0.1%Language:Objective-C 0.0%