Commit abd9f02e authored by DatHV's avatar DatHV
Browse files

update screen

parent e41fc4fe
import 'package:json_annotation/json_annotation.dart';
part 'onboarding_info_model.g.dart';
@JsonSerializable()
class OnboardingInfoModel {
final String? url;
final String? content;
OnboardingInfoModel({this.url, this.content});
factory OnboardingInfoModel.fromJson(Map<String, dynamic> json) => _$OnboardingInfoModelFromJson(json);
Map<String, dynamic> toJson() => _$OnboardingInfoModelToJson(this);
}
\ No newline at end of file
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'onboarding_info_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
OnboardingInfoModel _$OnboardingInfoModelFromJson(Map<String, dynamic> json) =>
OnboardingInfoModel(
url: json['url'] as String?,
content: json['content'] as String?,
);
Map<String, dynamic> _$OnboardingInfoModelToJson(
OnboardingInfoModel instance,
) => <String, dynamic>{'url': instance.url, 'content': instance.content};
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; // Hiển thị HTML
import 'package:get/get.dart';
import 'package:mypoint_flutter_app/base/base_response_model.dart';
import '../base/base_screen.dart';
import '../base/basic_state.dart';
import '../configs/constants.dart';
import '../resouce/base_color.dart';
import 'model/check_phone_response_model.dart';
import 'onboarding_viewmodel.dart';
class OnboardingScreen extends StatelessWidget {
final OnboardingViewModel controller = Get.find<OnboardingViewModel>();
class OnboardingScreen extends BaseScreen {
const OnboardingScreen({super.key});
@override
State<OnboardingScreen> createState() => _OnboardingScreenState();
}
class _OnboardingScreenState extends BaseState<OnboardingScreen> with BasicState {
final OnboardingViewModel _viewModel = Get.find<OnboardingViewModel>();
final FocusNode _focusNode = FocusNode();
void hideKeyboard() {
@override
void initState() {
super.initState();
_viewModel.fetchOnboardingContent();
_viewModel.checkPhoneRes.listen((response) {
_handleResponseCheckPhoneNumber(response.data);
_handleResponseError(response);
});
}
void _handleResponseError(BaseResponseModel<CheckPhoneResponseModel>? response) {
if (response?.isSuccess ?? false) return;
var errorCode = response?.errorCode ?? "";
var errorMessage = response?.errorMessage ?? Constants.commonError;
WidgetsBinding.instance.addPostFrameCallback((_) {
showAlertError(errorMessage);
if (errorCode == ErrorCodes.deviceLock) {
// show alert error popupErrorCSKH
return;
}
//show alert error popupError
});
}
void _handleResponseCheckPhoneNumber(CheckPhoneResponseModel? response) {
if (response == null) return;
if (response.requireRecaptcha == true) {
// show Captcha
return;
}
if ((response.mfaToken ?? "").isNotEmpty) {
// show OTP
return;
}
if (response.nextAction == "login") {
return;
}
if (response.nextAction == "signup") {
return;
}
}
void _hideKeyboard() {
FocusScope.of(Get.context!).unfocus();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: hideKeyboard,
Widget createBody() {
return GestureDetector(
onTap: _hideKeyboard,
child: Scaffold(
resizeToAvoidBottomInset: false,
body: Stack(
children: [
/// 📌 Hiển thị background từ API (hoặc ảnh mặc định)
Obx(() => Positioned.fill(
child: controller.backgroundUrl.value.isNotEmpty
? Image.network(controller.backgroundUrl.value, fit: BoxFit.cover)
: Image.asset("assets/images/bg_onboarding.png", fit: BoxFit.cover),
)),
Obx(
() => Positioned.fill(
child:
_viewModel.url.isNotEmpty
? Image.network(_viewModel.url, fit: BoxFit.cover)
: Image.asset("assets/images/bg_onboarding.png", fit: BoxFit.cover),
),
),
/// 📌 Nội dung chính
AnimatedPadding(
duration: const Duration(milliseconds: 300),
duration: const Duration(milliseconds: 120),
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
decoration: const BoxDecoration(
color: Colors.redAccent,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 32),
// decoration: const BoxDecoration(
// color: Colors.redAccent,
// borderRadius: BorderRadius.only(
// topLeft: Radius.circular(30),
// topRight: Radius.circular(30),
// ),
// ),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
/// 📌 Tiêu đề (Hiển thị nội dung HTML từ API hoặc mặc định)
Obx(() => Visibility(
visible: !_focusNode.hasFocus,
child: HtmlWidget(
controller.contentHtml.value.isNotEmpty
? controller.contentHtml.value
: """<h2 style="color: white;">Tiêu điểm dễ - Trừ tiền mê</h2>
Obx(
() => Visibility(
visible: !_focusNode.hasFocus,
child: HtmlWidget(
_viewModel.content.isNotEmpty
? _viewModel.content
: """<h4 style="color: white;">Tiêu điểm dễ - Trừ tiền mê</h4>
<p style="color: white;">Đừng bỏ lỡ cơ hội tích tới 30% tất cả giao dịch viễn thông
của các nhà mạng và đổi phiếu giảm giá tại hơn 200 thương hiệu được yêu thích nhất.</p>""",
textStyle: TextStyle(color: Colors.white),
),
),
)),
),
const SizedBox(height: 16),
/// 📌 Ô nhập số điện thoại
TextField(
inputFormatters: [
LengthLimitingTextInputFormatter(10)
],
// maxLength: 10,
focusNode: _focusNode,
keyboardType: TextInputType.phone,
style: const TextStyle(color: Colors.black),
style: const TextStyle(color: BaseColor.second600),
decoration: InputDecoration(
filled: true,
fillColor: Colors.white,
......@@ -72,55 +140,58 @@ class OnboardingScreen extends StatelessWidget {
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide.none,
),
prefixIcon: const Icon(Icons.phone, color: Colors.black),
prefixIcon: const Icon(Icons.phone, color: Color(0xFF9DA4AE))
),
onChanged: controller.updatePhoneNumber,
onChanged: _viewModel.updatePhoneNumber,
),
const SizedBox(height: 20),
const SizedBox(height: 16),
/// 📌 Nút Tiếp Tục
Obx(() => SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: controller.isButtonEnabled
? () {
debugPrint("Số điện thoại: ${controller.phoneNumber.value}");
}
: null,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 15),
backgroundColor: controller.isButtonEnabled ? Colors.white : Colors.white54,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
Obx(
() => SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed:
_viewModel.isButtonEnabled
? () {
_viewModel.checkPhoneNumber();
}
: null,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 15),
backgroundColor: _viewModel.isButtonEnabled ? Colors.white : Colors.white54,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
),
),
child: Text(
"Tiếp tục",
style: TextStyle(
color: controller.isButtonEnabled ? Colors.redAccent : Colors.grey,
fontSize: 18,
fontWeight: FontWeight.bold,
child: Text(
"Tiếp tục",
style: TextStyle(
color: _viewModel.isButtonEnabled ? BaseColor.second600 : BaseColor.second400,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
),
)),
const SizedBox(height: 10),
),
const SizedBox(height: 16),
/// 📌 Checkbox + Điều khoản sử dụng + Chính sách bảo mật
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Obx(() => Checkbox(
value: controller.isChecked.value,
onChanged: controller.toggleCheckbox,
activeColor: Colors.white,
checkColor: Colors.red,
side: const BorderSide(color: Colors.white, width: 2),
)),
Obx(
() => Checkbox(
value: _viewModel.isChecked.value,
onChanged: _viewModel.toggleCheckbox,
activeColor: Colors.white,
checkColor: Colors.red,
side: const BorderSide(color: Colors.white, width: 2),
),
),
Expanded(
child: RichText(
text: TextSpan(
style: const TextStyle(color: Colors.white70, fontSize: 12),
style: const TextStyle(color: Colors.white, fontSize: 14),
children: [
const TextSpan(text: "Bằng việc tiếp tục, bạn đã đọc và đồng ý với "),
WidgetSpan(
......@@ -130,6 +201,8 @@ class OnboardingScreen extends StatelessWidget {
"Điều khoản sử dụng",
style: TextStyle(
decoration: TextDecoration.underline,
decorationColor: Colors.white,
decorationThickness: 2,
color: Colors.white,
),
),
......@@ -143,6 +216,8 @@ class OnboardingScreen extends StatelessWidget {
"Chính sách bảo mật",
style: TextStyle(
decoration: TextDecoration.underline,
decorationColor: Colors.white,
decorationThickness: 2,
color: Colors.white,
),
),
......
import 'package:get/get.dart';
import 'package:mypoint_flutter_app/extensions/string_extension.dart';
import 'package:mypoint_flutter_app/networking/restful_api_request.dart';
import 'package:mypoint_flutter_app/onboading/model/check_phone_response_model.dart';
import '../base/base_response_model.dart';
import '../base/restful_api_viewmodel.dart';
import 'model/onboarding_info_model.dart';
class OnboardingViewModel extends GetxController {
class OnboardingViewModel extends RestfulApiViewModel {
var info = BaseResponseModel<OnboardingInfoModel>().obs;
var checkPhoneRes = BaseResponseModel<CheckPhoneResponseModel>().obs;
var phoneNumber = "".obs;
var isChecked = false.obs;
var backgroundUrl = "".obs; // URL ảnh nền
var contentHtml = "".obs; // Nội dung HTML
bool get isButtonEnabled => isChecked.value && phoneNumber.value.isPhoneValid();
String get content => info?.value?.data?.content ?? "";
String get url => info?.value?.data?.url ?? "";
/// Cập nhật số điện thoại
void updatePhoneNumber(String value) {
phoneNumber.value = value;
}
/// Cập nhật trạng thái checkbox
void toggleCheckbox(bool? value) {
isChecked.value = value!;
}
/// Gọi API để lấy dữ liệu
Future<void> fetchOnboardingContent() async {
var data = {"": ""};
backgroundUrl.value = data["url_background"] ?? backgroundUrl.value; // Nếu API có ảnh nền, thay thế
contentHtml.value = data["content"] ?? contentHtml.value; // Nếu API có nội dung, thay thế
client.getOnboardingInfo().then((value) {
info.value = value;
});
}
Future<void> checkPhoneNumber() async {
showLoading();
client.checkPhoneNumber(phoneNumber.value).then((value) {
checkPhoneRes.value = value;
hideLoading();
});
}
@override
......
import 'package:flutter/material.dart';
class BaseColor {
static const Color backgroundColor = Colors.green;
static const Color second25 = Color(0xFFFCFCFD);
static const Color second50 = Color(0xFFF9FAFB);
static const Color second100 = Color(0xFFF3F4F6);
static const Color second150 = Color(0xFFEBEDEF);
static const Color second200 = Color(0xFFE5E7EB);
static const Color second300 = Color(0xFFD2D6DB);
static const Color second400 = Color(0xFF9DA4AE);
static const Color second500 = Color(0xFF6C737F);
static const Color second600 = Color(0xFF4D5761);
static const Color second700 = Color(0xFF384250);
static const Color second800 = Color(0xFF1F2A37);
static const Color second900 = Color(0xFF111927);
static const Color primary50 = Color(0xFFFDE8EA);
static const Color primary100 = Color(0xFFFAD2D4);
static const Color primary150 = Color(0xFFF8BBBF);
static const Color primary200 = Color(0xFFF5A5A9);
static const Color primary300 = Color(0xFFF1777E);
static const Color primary400 = Color(0xFFEC4A53);
static const Color primary500 = Color(0xFFE71D28);
static const Color primary600 = Color(0xFFB91720);
static const Color primary00 = Color(0xFF8B1118);
static const Color primary800 = Color(0xFF5C0C10);
static const Color primary900 = Color(0xFF170304);
static const Color error25 = Color(0xFFFFFBFA);
static const Color error50 = Color(0xFFFEF3F2);
static const Color error100 = Color(0xFFFEE4E2);
static const Color error200 = Color(0xFFFECDCA);
static const Color error300 = Color(0xFFFDA29B);
static const Color error400 = Color(0xFFF97066);
static const Color error500 = Color(0xFFF04438);
static const Color error600 = Color(0xFFD92D20);
static const Color error700 = Color(0xFFB42318);
static const Color error800 = Color(0xFF912018);
static const Color error900 = Color(0xFF7A271A);
static const Color error950 = Color(0xFF55160C);
static const Color warning25 = Color(0xFFFFFCF5);
static const Color warning50 = Color(0xFFFFFAEB);
static const Color warning100 = Color(0xFFFEF0C7);
static const Color warning200 = Color(0xFFFEDF89);
static const Color warning300 = Color(0xFFFEC84B);
static const Color warning400 = Color(0xFFFDB022);
static const Color warning500 = Color(0xFFF79009);
static const Color warning600 = Color(0xFFDC6803);
static const Color warning700 = Color(0xFFB54708);
static const Color warning800 = Color(0xFF93370D);
static const Color warning900 = Color(0xFF7A2E0E);
static const Color warning950 = Color(0xFF4E1D09);
static const Color success25 = Color(0xFFF6FEF9);
static const Color success50 = Color(0xFFECFDF3);
static const Color success100 = Color(0xFFDCFAE6);
static const Color success200 = Color(0xFFABEFC6);
static const Color success300 = Color(0xFF75E0A7);
static const Color success400 = Color(0xFF47CD89);
static const Color success500 = Color(0xFF17B26A);
static const Color success600 = Color(0xFF079455);
static const Color success700 = Color(0xFF067647);
static const Color success800 = Color(0xFF085D3A);
static const Color success900 = Color(0xFF074D31);
static const Color success950 = Color(0xFF074D31);
}
\ No newline at end of file
const module = 'assets/images';
const String icClose = '$module/ic_close.svg';
const String icLogo = '$module/ic_logo.svg';
const String bgAlertHeader = '$module/bg_alert_header.svg';
\ No newline at end of file
import 'package:flutter/material.dart';
enum TextStyleIconAndText {
header1,
header2,
header3,
normal,
underline,
normalDisable,
normalButton,
}
TextStyle header1 = TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.black,
);
TextStyle header2 = TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black,
);
TextStyle header3 = TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
);
const TextStyle textNormal = TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Colors.black,
);
const TextStyle textMedium = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.black,
);
const TextStyle textSemiBold = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.black,
);
TextStyle textBold = TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.black,
);
TextStyle underline = TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
decoration: TextDecoration.underline,
color: Colors.blue,
);
TextStyle normalDisable = TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.grey,
);
TextStyle normalButton = TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white,
);
TextStyle getTextStyle(TextStyleIconAndText? textType) {
switch (textType) {
case TextStyleIconAndText.header1:
return header1;
case TextStyleIconAndText.header2:
return header2;
case TextStyleIconAndText.header3:
return header3;
case TextStyleIconAndText.normal:
return textNormal;
case TextStyleIconAndText.underline:
return underline;
case TextStyleIconAndText.normalDisable:
return normalDisable;
case TextStyleIconAndText.normalButton:
return normalButton;
default:
return textNormal;
}
}
\ No newline at end of file
......@@ -27,21 +27,6 @@ class _SplashScreenState extends State<SplashScreen> with ApiHelper {
super.initState();
initNetWork(APIPaths.baseUrl);
_viewModel.checkUpdateApp();
// ApiService().checkUpdateWithRequestManager().then((value) {
// if (value != null) {
// if (value!.status == UpdateStatus.force) {
// _showForceUpdateAlert();
// } else if (value!.status == UpdateStatus.suggest) {
// _showSuggestUpdateAlert();
// } else {
// _showForceUpdateAlert();
// // _navigateToHome();
// }
// } else {
// _showSuggestUpdateAlert();
// // _navigateToHome();
// }
// });
}
@override
......@@ -69,8 +54,7 @@ class _SplashScreenState extends State<SplashScreen> with ApiHelper {
} else if (status == UpdateStatus.suggest) {
_showSuggestUpdateAlert();
} else {
_showSuggestUpdateAlert();
// _navigateToBeforCheckUpdate();
_navigateToBeforCheckUpdate();
}
});
return Container(width: double.infinity, height: double.infinity, color: Colors.black.withOpacity(0.5));
......
......@@ -2,6 +2,7 @@ import 'package:get/get.dart';
import 'package:mypoint_flutter_app/base/restful_api_viewmodel.dart';
import 'package:mypoint_flutter_app/networking/restful_api_request.dart';
import '../base/base_response_model.dart';
import '../model/check_update_response_model.dart';
import '../model/update_response_object.dart';
class SplashScreenViewModel extends RestfulApiViewModel {
......
import 'package:flutter/material.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:get/get.dart';
import 'package:mypoint_flutter_app/resouce/define_image.dart';
import 'data_alert_model.dart';
enum ButtonsDirection { row, column }
class CustomAlertDialog extends StatelessWidget {
final DataAlertModel alertData;
final ButtonsDirection direction;
const CustomAlertDialog({super.key, required this.alertData, this.direction = ButtonsDirection.column});
@override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(16), color: Colors.white),
child: Stack(
children: [Container(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Header Image
if (alertData.background != null)
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.asset(alertData.background!),
// .network(
// alertData.background!,
// height: 120,
// width: 120,
// fit: BoxFit.cover,
// ),
),
const SizedBox(height: 10),
// Title
if (alertData.title != null)
Text(
alertData.title!,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
// HTML Content
if (alertData.content != null)
HtmlWidget(
alertData.content!,
),
// Html(
// data: alertData.content!,
// style: {
// "body": Style(
// textAlign: TextAlign.center,
// fontSize: FontSize.medium,
// ),
// },
// ),
const SizedBox(height: 10),
// Buttons
_buildButtons(),
],
),
),
// Close Button (X) ở góc phải trên
Positioned(
top: 0,
right: 0,
child: GestureDetector(
onTap: () => Get.back(),
child: const Icon(Icons.close, color: Colors.black, size: 24),
),
),
]
),
),
);
}
Widget _buildButtons() {
return direction == ButtonsDirection.column
? Column(
children:
alertData.buttons
?.map(
(btn) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: btn.bgColor,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
minimumSize: const Size(double.infinity, 48),
),
onPressed: btn.onPressed,
child: Text(
btn.text,
style: TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold),
),
),
),
)
.toList() ??
[],
)
: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children:
alertData.buttons
?.map(
(btn) => Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: btn.bgColor,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
minimumSize: const Size(100, 48),
),
onPressed: btn.onPressed,
child: Text(
btn.text,
style: TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold),
),
),
),
),
)
.toList() ??
[],
);
}
}
import 'dart:ui';
import 'package:flutter/material.dart';
import '../../resouce/base_color.dart';
class DataAlertModel {
final String? background;
final String? title;
final String? content;
final List<AlertButton>? buttons;
DataAlertModel({
this.background,
this.title,
this.content,
this.buttons = const [],
});
}
class AlertButton {
final String text;
final Color textColor;
final Color bgColor;
final bool isPrimary;
final VoidCallback onPressed;
AlertButton({
required this.text,
required this.textColor,
required this.bgColor,
required this.isPrimary,
required this.onPressed,
});
}
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:mypoint_flutter_app/resouce/define_image.dart';
import 'alert_style.dart';
import 'animation_transition.dart';
import 'dialog_button.dart';
class Alert {
final String? id;
final BuildContext context;
final AlertType? type;
final AlertStyle style;
final EdgeInsets? padding;
final Widget? image;
final String? urlImage;
final String? title;
final String? desc;
final Widget content;
final List<DialogButton>? buttons;
final Function? closeFunction;
final Widget? closeIcon;
final bool onWillPopActive;
final bool useRootNavigator;
final AlertAnimation? alertAnimation;
Alert({
required this.context,
this.id,
this.type,
this.style = const AlertStyle(),
this.padding,
this.image,
this.urlImage,
this.title,
this.desc,
this.content = const SizedBox(),
this.buttons,
this.closeFunction,
this.closeIcon,
this.onWillPopActive = false,
this.alertAnimation,
this.useRootNavigator = true,
});
/// Displays defined alert window
Future<bool?> show() async {
return await showGeneralDialog(
context: context,
pageBuilder: (BuildContext buildContext, Animation<double> animation,
Animation<double> secondaryAnimation) {
return _buildDialog();
},
barrierDismissible: style.isOverlayTapDismiss,
barrierLabel:
MaterialLocalizations.of(context).modalBarrierDismissLabel,
barrierColor: style.overlayColor,
useRootNavigator: useRootNavigator,
transitionDuration: style.animationDuration,
transitionBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) =>
alertAnimation == null
? _showAnimation(animation, secondaryAnimation, child)
: alertAnimation!(
context, animation, secondaryAnimation, child));
}
/// Dismisses the alert dialog.
Future<void> dismiss() async {
Navigator.of(context, rootNavigator: useRootNavigator).pop();
}
/// Alert dialog content widget
Widget _buildDialog() {
final Widget child = Align(
alignment: style.alertAlignment,
child: ConstrainedBox(
constraints: style.constraints ?? BoxConstraints.loose(Size.infinite),
child: SingleChildScrollView(
child: AlertDialog(
key: id == null ? null : Key(id!),
backgroundColor: style.backgroundColor ??
Theme.of(context).dialogTheme.backgroundColor,
shape: style.alertBorder ?? _defaultShape(),
insetPadding: style.alertPadding,
elevation: style.alertElevation,
titlePadding: const EdgeInsets.all(0.0),
title: Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_getCloseButton(),
Padding(
padding: padding ??
EdgeInsets.fromLTRB(
20, (style.isCloseButton ? 0 : 10), 20, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Center(
child: _getImage(),
),
if (title != null)
_getTextWidget(
style.isTitleSelectable,
title,
style.titleStyle,
style.titleTextAlign,
style.titlePadding ??
EdgeInsets.only(
top: 15,
bottom: desc == null ? 0 : 10,
),
),
if (desc != null)
_getTextWidget(
style.isDescSelectable,
desc,
style.descStyle,
style.descTextAlign,
style.descPadding,
),
content,
],
),
)
],
),
),
),
contentPadding: style.buttonAreaPadding,
content: style.buttonsDirection == ButtonsDirection.row
? Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: _getButtons(),
)
: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: _getButtons(),
)),
),
),
);
return onWillPopActive
? PopScope(child: child) //WillPopScope(onWillPop: () async => false, child: child)
: child;
}
/// Returns Text Widget or Selectable Text Widget with Padding
/// based on isSelectable property.
Widget _getTextWidget(bool isSelectable, String? text, TextStyle textStyle,
TextAlign textAlign, EdgeInsets edgeInsets) {
return Padding(
padding: edgeInsets,
child: _getTextWidgetBySelectable(
isSelectable,
text,
textStyle,
textAlign,
),
);
}
/// Returns Text Widget or Selectable Text Widget
/// based on isSelectable property.
Widget _getTextWidgetBySelectable(bool isSelectable, String? text,
TextStyle textStyle, TextAlign textAlign) {
if (isSelectable) {
return SelectableText(
text ?? "",
style: textStyle,
textAlign: textAlign,
);
} else {
return Text(
text ?? "",
style: textStyle,
textAlign: textAlign,
);
}
}
/// Returns the close button on the top right
Widget _getCloseButton() {
return style.isCloseButton
? Padding(
padding: const EdgeInsets.fromLTRB(0, 10, 10, 0),
child: GestureDetector(
onTap: () {
if (closeFunction == null) {
Navigator.of(context, rootNavigator: useRootNavigator).pop();
} else {
closeFunction!();
}
},
child: Container(
alignment: FractionalOffset.topRight,
child: closeIcon != null
? Container(child: closeIcon)
: Container(
width: 20,
height: 20,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(icClose),
),
),
),
),
),
)
: Container();
}
/// Returns defined buttons. Default: Cancel Button
List<Widget> _getButtons() {
List<Widget> expandedButtons = [];
if (style.isButtonVisible) {
if (buttons != null) {
for (var button in buttons!) {
var buttonWidget = Padding(
padding: const EdgeInsets.only(left: 2, right: 2),
child: button,
);
if ((button.width != null && buttons!.length == 1) ||
style.buttonsDirection == ButtonsDirection.column) {
expandedButtons.add(buttonWidget);
} else {
expandedButtons.add(Expanded(
child: buttonWidget,
));
}
}
} else {
Widget cancelButton = DialogButton(
child: Text(
"CANCEL",
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: () => Navigator.pop(context),
);
if (style.buttonsDirection == ButtonsDirection.row) {
cancelButton = Expanded(
child: cancelButton,
);
}
expandedButtons.add(cancelButton);
}
}
return expandedButtons;
}
/// Returns alert default border style
ShapeBorder _defaultShape() {
return RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
side: BorderSide(
color: Colors.blueGrey,
),
);
}
/// Returns alert image for icon
Widget _getImage() {
Widget response;
if (urlImage != null && urlImage!.isNotEmpty) {
response = Image.network(urlImage!);
return response;
}
switch (type) {
case AlertType.success:
response = Image.asset("");
break;
case AlertType.error:
response = Image.asset("");
break;
case AlertType.info:
response = Image.asset("");
break;
case AlertType.warning:
response = Image.asset("");
break;
case AlertType.none:
response = Container();
break;
default:
response = image ?? Container();
break;
}
return response;
}
/// Shows alert with selected animation
_showAnimation(animation, secondaryAnimation, child) {
return AnimationTransition.fromBottom(animation, secondaryAnimation, child);
}
}
import 'package:flutter/material.dart';
enum AlertType { error, success, info, warning, none }
/// Buttons container
enum ButtonsDirection { row, column }
/// Defines Default Alert Window Padding
const EdgeInsets defaultAlertPadding = EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0);
typedef AlertAnimation = Widget Function(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
);
/// Alert style class for reusable customization of dialogs.
class AlertStyle {
/// The [animationDuration] parameter is used to set the animation transition time. Default: "200 ms"
final Duration animationDuration;
/// The [alertBorder] parameter sets border.
final ShapeBorder? alertBorder;
/// The [isButtonVisible] paramater is used to decide hide or display buttons
final bool isButtonVisible;
/// The [isCloseButton] parameter sets visibility of the close button. Default: "true"
final bool isCloseButton;
/// The [isOverlayTapDismiss] parameter sets closing the alert by clicking outside. Default: "true"
final bool isOverlayTapDismiss;
/// The [backgroundColor] parameter sets the background color.
final Color? backgroundColor;
/// The [overlayColor] parameter sets the background color of the outside. Default: "Color(0xDD000000)"
final Color overlayColor;
/// The [titleStyle] parameter sets alert title text style.
final TextStyle titleStyle;
/// The [descStyle] parameter sets alert desc text style.
final TextStyle descStyle;
/// The [titleTextAlign] parameter sets alignment of the title.
final TextAlign titleTextAlign;
/// The [descTextAlign] parameter sets alignment of the desc.
final TextAlign descTextAlign;
/// The [buttonAreaPadding] parameter sets button area padding.
final EdgeInsets buttonAreaPadding;
/// The [constraints] parameter sets Alert size.
final BoxConstraints? constraints;
/// The [buttonsDirection] parameter sets button container as Row or Col.
final ButtonsDirection buttonsDirection;
/// The [alertElevation] parameter sets elevation of alert dialog container.
final double? alertElevation;
/// The [alertPadding] parameter sets alert area padding.
final EdgeInsets alertPadding;
/// The [alertAlignment] parameter sets alert dialog alignment.
final AlignmentGeometry alertAlignment;
/// The [isTitleSelectable] parameter sets title text is selectable or not.
final bool isTitleSelectable;
/// The [isDescSelectable] parameter sets desc text is selectable or not.
final bool isDescSelectable;
/// The [titlePadding] parameter sets title area padding.
final EdgeInsets? titlePadding;
/// The [descPadding] parameter sets desc area padding.
final EdgeInsets descPadding;
/// Alert style constructor function
/// All properties are optional.
const AlertStyle({
this.animationDuration = const Duration(milliseconds: 200),
this.alertBorder,
this.isButtonVisible = true,
this.isCloseButton = true,
this.isOverlayTapDismiss = true,
this.backgroundColor,
this.overlayColor = Colors.black87,
this.titleStyle = const TextStyle(
color: Colors.black,
fontWeight: FontWeight.w500,
fontStyle: FontStyle.normal),
this.titleTextAlign = TextAlign.center,
this.descStyle = const TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
fontStyle: FontStyle.normal),
this.descTextAlign = TextAlign.center,
this.buttonAreaPadding = const EdgeInsets.all(20.0),
this.constraints,
this.buttonsDirection = ButtonsDirection.row,
this.alertElevation,
this.alertPadding = defaultAlertPadding,
this.alertAlignment = Alignment.center,
this.isTitleSelectable = false,
this.isDescSelectable = false,
this.titlePadding,
this.descPadding = const EdgeInsets.all(0.0),
});
}
import 'package:flutter/material.dart';
/// Predefined functions for transition animations
///
/// Exp: AnimationTransition.fromRight(animation, secondaryAnimation, child);
class AnimationTransition {
/// Slide animation, from right to left (SlideTransition)
static fromRight(Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: child,
);
}
/// Slide animation, from left to right (SlideTransition)
static fromLeft(Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: child,
);
}
/// Slide animation, from top to bottom (SlideTransition)
static fromTop(Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, -1.0),
end: Offset.zero,
).animate(animation),
child: child,
);
}
/// Slide animation, from top to bottom (SlideTransition)
static fromBottom(Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: Offset.zero,
).animate(animation),
child: child,
);
}
/// Scale animation, from in to out (ScaleTransition)
static grow(Animation<double> animation, Animation<double> secondaryAnimation,
Widget child) {
return ScaleTransition(
scale: Tween<double>(
begin: 0.0,
end: 1.0,
).animate(
CurvedAnimation(
parent: animation,
curve: Interval(
0.00,
0.50,
curve: Curves.linear,
),
),
),
child: child,
);
}
/// Scale animation, from out to in (ScaleTransition)
static shrink(Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return ScaleTransition(
scale: Tween<double>(
begin: 1.2,
end: 1.0,
).animate(
CurvedAnimation(
parent: animation,
curve: Interval(0.5, 1.0),
// curve: Interval(0,
// 0.50,
// 1.00,
// curve: Curves.linear,
// ),
),
),
child: child,
);
}
}
/*
* rflutter_alert
* Created by Ratel
* https://ratel.com.tr
*
* Copyright (c) 2018 Ratel, LLC. All rights reserved.
* See LICENSE for distribution and usage details.
*/
import 'package:flutter/material.dart';
/// Used for defining alert buttons.
///
/// [child] and [onPressed] parameters are required.
class DialogButton extends StatelessWidget {
final Widget? child;
final double? width;
final double height;
final Color? color;
final Color? highlightColor;
final Color? splashColor;
final Gradient? gradient;
final BorderRadius radius;
final VoidCallback? onPressed;
final BoxBorder border;
final EdgeInsets padding;
final EdgeInsets margin;
/// DialogButton constructor
const DialogButton({
super.key,
required this.child,
this.width,
this.height = 50.0,
this.color,
this.highlightColor,
this.splashColor,
this.gradient,
this.radius = const BorderRadius.all(Radius.circular(20)),
this.border = const Border.fromBorderSide(
BorderSide(
color: Color(0x00000000),
width: 0,
style: BorderStyle.solid,
),
),
this.padding = const EdgeInsets.only(left: 6, right: 6),
this.margin = const EdgeInsets.all(6),
required this.onPressed,
});
/// Creates alert buttons based on constructor params
@override
Widget build(BuildContext context) {
return Container(
margin: margin,
width: width,
height: height,
decoration: BoxDecoration(
color: color ?? Theme.of(context).colorScheme.secondary,
gradient: gradient,
borderRadius: radius,
border: border,
),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: radius,
highlightColor: highlightColor ?? Theme.of(context).highlightColor,
splashColor: splashColor ?? Theme.of(context).splashColor,
onTap: onPressed,
child: Padding(
padding: padding,
child: Center(
child: child,
),
),
),
),
);
}
}
import 'package:flutter/material.dart';
class ButtonContainer extends StatelessWidget {
final Color color;
final double cornerRadius;
final double? height;
final double? width;
final Widget child;
final bool visible;
const ButtonContainer({
super.key,
required this.child,
this.height,
this.width,
this.color = Colors.white,
this.cornerRadius = 8,
this.visible = true,
});
@override
Widget build(BuildContext context) {
return Visibility(
visible: visible,
child: Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(cornerRadius), color: color),
width: width,
height: height,
child: child,
),
);
}
}
......@@ -41,6 +41,10 @@ dependencies:
fluttertoast: 8.2.12
logger:
url_launcher: ^6.3.1
flutter_widget_from_html: ^0.16.0
device_info_plus: ^9.0.3
uuid: ^4.3.3
flutter_svg:
dev_dependencies:
flutter_test:
......@@ -70,8 +74,7 @@ flutter:
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/splash_screen.png
- assets/images/bg_onboarding.png
- assets/images/
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment