fix : splash animation
feat : auth with password chore : app Architecture
This commit is contained in:
8
assets/icons/call.svg
Normal file
8
assets/icons/call.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="vuesax/bold/call">
|
||||
<g id="call">
|
||||
<path id="Vector" d="M11.05 14.95L9.2 16.8C8.81 17.19 8.19 17.19 7.79 16.81C7.68 16.7 7.57 16.6 7.46 16.49C6.43 15.45 5.5 14.36 4.67 13.22C3.85 12.08 3.19 10.94 2.71 9.81C2.24 8.67 2 7.58 2 6.54C2 5.86 2.12 5.21 2.36 4.61C2.6 4 2.98 3.44 3.51 2.94C4.15 2.31 4.85 2 5.59 2C5.87 2 6.15 2.06 6.4 2.18C6.66 2.3 6.89 2.48 7.07 2.74L9.39 6.01C9.57 6.26 9.7 6.49 9.79 6.71C9.88 6.92 9.93 7.13 9.93 7.32C9.93 7.56 9.86 7.8 9.72 8.03C9.59 8.26 9.4 8.5 9.16 8.74L8.4 9.53C8.29 9.64 8.24 9.77 8.24 9.93C8.24 10.01 8.25 10.08 8.27 10.16C8.3 10.24 8.33 10.3 8.35 10.36C8.53 10.69 8.84 11.12 9.28 11.64C9.73 12.16 10.21 12.69 10.73 13.22C10.83 13.32 10.94 13.42 11.04 13.52C11.44 13.91 11.45 14.55 11.05 14.95Z" fill="#B0B0B0"/>
|
||||
<path id="Vector_2" d="M21.97 18.33C21.97 18.61 21.92 18.9 21.82 19.18C21.79 19.26 21.76 19.34 21.72 19.42C21.55 19.78 21.33 20.12 21.04 20.44C20.55 20.98 20.01 21.37 19.4 21.62C19.39 21.62 19.38 21.63 19.37 21.63C18.78 21.87 18.14 22 17.45 22C16.43 22 15.34 21.76 14.19 21.27C13.04 20.78 11.89 20.12 10.75 19.29C10.36 19 9.96998 18.71 9.59998 18.4L12.87 15.13C13.15 15.34 13.4 15.5 13.61 15.61C13.66 15.63 13.72 15.66 13.79 15.69C13.87 15.72 13.95 15.73 14.04 15.73C14.21 15.73 14.34 15.67 14.45 15.56L15.21 14.81C15.46 14.56 15.7 14.37 15.93 14.25C16.16 14.11 16.39 14.04 16.64 14.04C16.83 14.04 17.03 14.08 17.25 14.17C17.47 14.26 17.7 14.39 17.95 14.56L21.26 16.91C21.52 17.09 21.7 17.3 21.81 17.55C21.91 17.8 21.97 18.05 21.97 18.33Z" fill="#B0B0B0"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
3
assets/icons/key.svg
Normal file
3
assets/icons/key.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="13" viewBox="0 0 24 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path id="key" d="M6.26087 8.32828C5.68696 8.32828 5.19565 8.12441 4.78696 7.71667C4.37826 7.30893 4.17391 6.81878 4.17391 6.24621C4.17391 5.67364 4.37826 5.18348 4.78696 4.77575C5.19565 4.36801 5.68696 4.16414 6.26087 4.16414C6.83478 4.16414 7.32609 4.36801 7.73478 4.77575C8.14348 5.18348 8.34783 5.67364 8.34783 6.24621C8.34783 6.81878 8.14348 7.30893 7.73478 7.71667C7.32609 8.12441 6.83478 8.32828 6.26087 8.32828ZM6.26087 12.4924C4.52174 12.4924 3.04348 11.8851 1.82609 10.6706C0.608696 9.45606 0 7.98126 0 6.24621C0 4.51115 0.608696 3.03635 1.82609 1.82181C3.04348 0.60727 4.52174 0 6.26087 0C7.42609 0 8.48261 0.286284 9.43044 0.858853C10.3783 1.43142 11.1304 2.18617 11.687 3.1231H20.8696L24 6.24621L19.3043 10.9309L17.2174 9.36931L15.1304 10.9309L12.913 9.36931H11.687C11.1304 10.3062 10.3783 11.061 9.43044 11.6336C8.48261 12.2061 7.42609 12.4924 6.26087 12.4924ZM6.26087 10.4103C7.23478 10.4103 8.0913 10.1154 8.83044 9.52547C9.56957 8.93555 10.0609 8.18947 10.3043 7.28724H13.5652L15.0783 8.3543L17.2174 6.76672L19.0696 8.19815L21.0261 6.24621L19.9826 5.20517H10.3043C10.0609 4.30294 9.56957 3.55687 8.83044 2.96695C8.0913 2.37703 7.23478 2.08207 6.26087 2.08207C5.11304 2.08207 4.13043 2.48981 3.31304 3.30528C2.49565 4.12076 2.08696 5.10107 2.08696 6.24621C2.08696 7.39134 2.49565 8.37165 3.31304 9.18713C4.13043 10.0026 5.11304 10.4103 6.26087 10.4103Z" fill="#B8B8B8"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1
assets/icons/user.svg
Normal file
1
assets/icons/user.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" enable-background="new 0 0 100 100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-.051" x2="91.965" y1="-4.851" y2="95.99"><stop offset="0" stop-color="#97e0ff"/><stop offset="1" stop-color="#1075ff"/></linearGradient><path d="m50 2.5000012c-26.2000008 0-47.5 21.2999973-47.5 47.5s21.2999992 47.4999988 47.5 47.4999988 47.5-21.3000031 47.5-47.5-21.3000031-47.4999988-47.5-47.4999988zm0 28.1999996c8.2000008 0 14.9000015 6.7000008 14.9000015 14.8999977s-6.7000007 14.9000015-14.9000015 14.9000015-14.9000015-6.7000008-14.9000015-14.9000015 6.7000007-14.8999977 14.9000015-14.8999977zm-26 48.1000023v-2.1999969c0-6.3000031 5.1000004-11.5 11.5-11.5h29c6.3000031 0 11.5 5.0999985 11.5 11.5v2.1999969c-6.9000015 6.1999969-16 10-26 10s-19.1000004-3.8000031-26-10z" fill="url(#SVGID_1_)"/></svg>
|
||||
|
After Width: | Height: | Size: 926 B |
BIN
assets/vec/call.svg.vec
Normal file
BIN
assets/vec/call.svg.vec
Normal file
Binary file not shown.
BIN
assets/vec/key.svg.vec
Normal file
BIN
assets/vec/key.svg.vec
Normal file
Binary file not shown.
12
lib/data/data_provider/local_storage/hive/hive_provider.dart
Normal file
12
lib/data/data_provider/local_storage/hive/hive_provider.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'package:hive_ce_flutter/hive_flutter.dart';
|
||||
import 'package:rasadyar_app/data/data_provider/local_storage/i_local_storage_provider.dart';
|
||||
|
||||
|
||||
enum HiveBoxNames { user, settings, auth }
|
||||
|
||||
class HiveProvider extends ILocalStorageProvider {
|
||||
@override
|
||||
Future<void> init() async {
|
||||
await Hive.initFlutter();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
const int userTypeId = 0;
|
||||
@@ -0,0 +1,3 @@
|
||||
abstract class ILocalStorageProvider {
|
||||
Future<void> init();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:hive_ce_flutter/hive_flutter.dart';
|
||||
import 'package:rasadyar_app/data/data_provider/local_storage/hive/hive_provider.dart';
|
||||
|
||||
abstract class IUserLocalStorage {
|
||||
Future<bool> userAuthed();
|
||||
}
|
||||
|
||||
class UserLocalStorage extends IUserLocalStorage {
|
||||
final user = Hive.box(HiveBoxNames.user.name);
|
||||
|
||||
@override
|
||||
Future<bool> userAuthed() async {
|
||||
if (user.isNotEmpty ) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
17
lib/data/model/user/user_model.dart
Normal file
17
lib/data/model/user/user_model.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'package:hive_ce/hive.dart';
|
||||
|
||||
import '../../data_provider/local_storage/hive/hive_types.dart';
|
||||
|
||||
|
||||
part 'user_model.g.dart';
|
||||
|
||||
@HiveType(typeId: userTypeId)
|
||||
class UserModel extends HiveObject{
|
||||
@HiveField(0)
|
||||
String? token;
|
||||
|
||||
@HiveField(1)
|
||||
String? refreshToken;
|
||||
|
||||
UserModel({this.token, this.refreshToken});
|
||||
}
|
||||
44
lib/data/model/user/user_model.g.dart
Normal file
44
lib/data/model/user/user_model.g.dart
Normal file
@@ -0,0 +1,44 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'user_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class UserModelAdapter extends TypeAdapter<UserModel> {
|
||||
@override
|
||||
final int typeId = 0;
|
||||
|
||||
@override
|
||||
UserModel read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return UserModel(
|
||||
token: fields[0] as String?,
|
||||
refreshToken: fields[1] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, UserModel obj) {
|
||||
writer
|
||||
..writeByte(2)
|
||||
..writeByte(0)
|
||||
..write(obj.token)
|
||||
..writeByte(1)
|
||||
..write(obj.refreshToken);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is UserModelAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
6
lib/domain/entity/user/user_entity.dart
Normal file
6
lib/domain/entity/user/user_entity.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
class UserEntity {
|
||||
String? token;
|
||||
String? refreshToken;
|
||||
|
||||
UserEntity({this.token, this.refreshToken});
|
||||
}
|
||||
25
lib/domain/repository/user/user_repository.dart
Normal file
25
lib/domain/repository/user/user_repository.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
import 'package:rasadyar_app/data/data_source/local_storage/user/user_local_storage.dart';
|
||||
|
||||
abstract class IUserRepository {
|
||||
Future<bool> userAuthed();
|
||||
/*Future<void> setUserAuthed(bool value);
|
||||
Future<void> setUserName(String name);
|
||||
Future<String?> getUserName();
|
||||
Future<void> setUserPhone(String phone);
|
||||
Future<String?> getUserPhone();
|
||||
Future<void> setUserEmail(String email);
|
||||
Future<String?> getUserEmail();
|
||||
Future<void> setUserPassword(String password);
|
||||
Future<String?> getUserPassword();*/
|
||||
}
|
||||
|
||||
class UserRepository implements IUserRepository {
|
||||
final IUserLocalStorage _userLocalStorage;
|
||||
|
||||
UserRepository(this._userLocalStorage);
|
||||
|
||||
@override
|
||||
Future<bool> userAuthed() async {
|
||||
return await _userLocalStorage.userAuthed();
|
||||
}
|
||||
}
|
||||
17
lib/domain/service/user/user_service.dart
Normal file
17
lib/domain/service/user/user_service.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:rasadyar_app/domain/repository/user/user_repository.dart';
|
||||
import 'package:rasadyar_app/infrastructure/di/di.dart';
|
||||
|
||||
class UserService extends GetxService {
|
||||
late IUserRepository _userLocalStorage;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
return super.onInit();
|
||||
_userLocalStorage = di.get<UserRepository>();
|
||||
}
|
||||
|
||||
Future<bool> isUserAuthed() async {
|
||||
return await _userLocalStorage.userAuthed();
|
||||
}
|
||||
}
|
||||
35
lib/infrastructure/di/di.dart
Normal file
35
lib/infrastructure/di/di.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:hive_ce_flutter/hive_flutter.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:rasadyar_app/data/data_provider/local_storage/hive/hive_provider.dart';
|
||||
import 'package:rasadyar_app/data/data_source/local_storage/user/user_local_storage.dart';
|
||||
import 'package:rasadyar_app/data/model/user/user_model.dart';
|
||||
import 'package:rasadyar_app/domain/repository/user/user_repository.dart';
|
||||
|
||||
final di = GetIt.instance;
|
||||
|
||||
void setupInjection() {
|
||||
di.registerLazySingleton(() => HiveProvider(), instanceName: 'HiveProvider');
|
||||
di.registerSingleton<Logger>( Logger());
|
||||
}
|
||||
|
||||
Future<void> setupAllProvider() async {
|
||||
await _setupLocalStorage();
|
||||
await di.allReady();
|
||||
}
|
||||
|
||||
Future<void> _setupLocalStorage() async {
|
||||
final hiveProvider = di.get<HiveProvider>(instanceName: 'HiveProvider');
|
||||
await hiveProvider.init();
|
||||
Hive.registerAdapter(UserModelAdapter());
|
||||
|
||||
await Hive.openBox<UserModel>(HiveBoxNames.user.name);
|
||||
|
||||
//user
|
||||
di.registerLazySingleton<IUserLocalStorage>(() => UserLocalStorage());
|
||||
di.registerLazySingleton<IUserRepository>(
|
||||
() => UserRepository(di.get<IUserLocalStorage>()),
|
||||
);
|
||||
|
||||
//
|
||||
}
|
||||
284
lib/main.dart
284
lib/main.dart
@@ -1,19 +1,16 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:rasadyar_app/domain/service/user/user_service.dart';
|
||||
import 'package:rasadyar_app/infrastructure/di/di.dart';
|
||||
import 'package:rasadyar_app/presentation/common/app_color.dart';
|
||||
import 'package:rasadyar_app/presentation/common/app_fonts.dart';
|
||||
import 'package:rasadyar_app/presentation/utils/color_utils.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/buttons/elevated.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/buttons/fab_outlined.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/buttons/outline_elevated.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/buttons/text_button.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/inputs/r_input.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/pagination/pagination_from_until.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/pagination/show_more.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/tabs/tab.dart';
|
||||
import 'package:rasadyar_app/presentation/routes/app_pages.dart';
|
||||
|
||||
import 'presentation/widget/buttons/fab.dart';
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
setupInjection();
|
||||
await setupAllProvider();
|
||||
|
||||
void main() {
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
@@ -22,264 +19,15 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
return GetMaterialApp(
|
||||
title: 'رصدیار',
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
),
|
||||
home: Home(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Home extends StatefulWidget {
|
||||
const Home({super.key});
|
||||
|
||||
@override
|
||||
State<Home> createState() => _HomeState();
|
||||
}
|
||||
|
||||
class _HomeState extends State<Home> {
|
||||
List<bool> _isOpen = [false, false, false, false, false, false];
|
||||
|
||||
void _handleAdd() {
|
||||
print("Add FAB pressed");
|
||||
}
|
||||
|
||||
void _handleEdit() {
|
||||
print("Edit FAB pressed");
|
||||
}
|
||||
|
||||
void _handleDelete() {
|
||||
print("Delete FAB pressed");
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text("System design"), centerTitle: true),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ExpansionPanelList(
|
||||
expansionCallback: (panelIndex, isExpanded) {
|
||||
setState(() {
|
||||
_isOpen[panelIndex] = isExpanded;
|
||||
});
|
||||
},
|
||||
children: [
|
||||
buttonWidget(),
|
||||
fabWidget(),
|
||||
outlinedFabWidget(),
|
||||
paginationWidget(),
|
||||
tabWidget(),
|
||||
inputsWidget(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ExpansionPanel inputsWidget() {
|
||||
return ExpansionPanel(
|
||||
isExpanded: _isOpen[5],
|
||||
headerBuilder: (context, isExpanded) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
"inputs",
|
||||
style: AppFonts.yekan20Regular.copyWith(color: Colors.red),
|
||||
),
|
||||
);
|
||||
},
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
spacing: 14,
|
||||
children: [
|
||||
RTextField(
|
||||
hintText: 'حجم کشتار را در روز به قطعه وارد کنید',
|
||||
hintStyle: AppFonts.yekan13Regular,
|
||||
),
|
||||
RTextField(
|
||||
label: 'تلفن مرغداری',
|
||||
labelStyle: AppFonts.yekan10Regular,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ExpansionPanel tabWidget() {
|
||||
return ExpansionPanel(
|
||||
isExpanded: _isOpen[4],
|
||||
headerBuilder: (context, isExpanded) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
"tab",
|
||||
style: AppFonts.yekan20Regular.copyWith(color: Colors.red),
|
||||
),
|
||||
);
|
||||
},
|
||||
body: Column(
|
||||
spacing: 14,
|
||||
children: [
|
||||
CupertinoSegmentedControlDemo(),
|
||||
CupertinoSegmentedControlDemo2(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ExpansionPanel paginationWidget() {
|
||||
return ExpansionPanel(
|
||||
isExpanded: _isOpen[3],
|
||||
headerBuilder: (context, isExpanded) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
"پیجینیشن",
|
||||
style: AppFonts.yekan20Regular.copyWith(color: Colors.red),
|
||||
),
|
||||
);
|
||||
},
|
||||
body: Column(spacing: 14, children: [RShowMore(), PaginationFromUntil()]),
|
||||
);
|
||||
}
|
||||
|
||||
ExpansionPanel outlinedFabWidget() {
|
||||
return ExpansionPanel(
|
||||
isExpanded: _isOpen[2],
|
||||
headerBuilder: (context, isExpanded) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
"Outlined Fab ",
|
||||
style: AppFonts.yekan20Regular.copyWith(color: Colors.green),
|
||||
),
|
||||
);
|
||||
},
|
||||
body: Column(
|
||||
spacing: 14,
|
||||
children: [
|
||||
Row(),
|
||||
|
||||
RFabOutlined.smallAdd(onPressed: () {}),
|
||||
RFabOutlined.smallAdd(onPressed: null),
|
||||
|
||||
RFabOutlined.smallAddNoBorder(onPressed: () {}),
|
||||
RFabOutlined.smallAddNoBorder(onPressed: null),
|
||||
|
||||
RFabOutlined.add(onPressed: () {}),
|
||||
RFabOutlined.add(onPressed: null),
|
||||
|
||||
RFabOutlined.addNoBorder(onPressed: () {}),
|
||||
RFabOutlined.addNoBorder(onPressed: null),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ExpansionPanel fabWidget() {
|
||||
return ExpansionPanel(
|
||||
isExpanded: _isOpen[1],
|
||||
headerBuilder: (context, isExpanded) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
"Fab",
|
||||
style: AppFonts.yekan20Regular.copyWith(color: Colors.green),
|
||||
),
|
||||
);
|
||||
},
|
||||
body: Column(
|
||||
spacing: 14,
|
||||
children: [
|
||||
Row(),
|
||||
|
||||
RFab.smallAdd(onPressed: () {}),
|
||||
RFab.smallAdd(onPressed: null),
|
||||
|
||||
RFab.add(onPressed: () {}),
|
||||
RFab.add(onPressed: null),
|
||||
|
||||
RFab.smallEdit(onPressed: null),
|
||||
RFab.smallEdit(onPressed: () {}),
|
||||
|
||||
RFab.edit(onPressed: () {}),
|
||||
RFab.edit(onPressed: null),
|
||||
|
||||
RFab.smallDelete(onPressed: () {}),
|
||||
RFab.smallDelete(onPressed: null),
|
||||
|
||||
RFab.delete(onPressed: () {}),
|
||||
RFab.delete(onPressed: null),
|
||||
|
||||
RFab.smallAction(onPressed: () {}),
|
||||
RFab.smallAction(onPressed: null),
|
||||
|
||||
RFab.action(onPressed: () {}),
|
||||
RFab.action(onPressed: null),
|
||||
|
||||
RFab.smallFilter(onPressed: () {}),
|
||||
RFab.smallFilter(onPressed: null),
|
||||
|
||||
RFab.filter(onPressed: () {}),
|
||||
RFab.filter(onPressed: null),
|
||||
|
||||
RFab.smallDownload(onPressed: () {}),
|
||||
RFab.smallDownload(onPressed: null),
|
||||
|
||||
RFab.download(onPressed: () {}),
|
||||
RFab.download(onPressed: null),
|
||||
|
||||
RFab.smallExcel(onPressed: () {}),
|
||||
RFab.smallExcel(onPressed: null),
|
||||
|
||||
RFab.excel(onPressed: () {}),
|
||||
RFab.excel(onPressed: null),
|
||||
|
||||
RFab.smallBack(onPressed: () {}),
|
||||
RFab.smallBack(onPressed: null),
|
||||
|
||||
RFab.back(onPressed: () {}),
|
||||
RFab.back(onPressed: null),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ExpansionPanel buttonWidget() {
|
||||
return ExpansionPanel(
|
||||
isExpanded: _isOpen[0],
|
||||
headerBuilder: (context, isExpanded) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
"دکمه ها",
|
||||
style: AppFonts.yekan20Regular.copyWith(color: Colors.green),
|
||||
),
|
||||
);
|
||||
},
|
||||
body: Column(
|
||||
spacing: 14,
|
||||
children: [
|
||||
Row(),
|
||||
|
||||
RElevated(text: 'ثبت', onPressed: () {}),
|
||||
|
||||
RElevated(text: 'ثبت', onPressed: null),
|
||||
|
||||
ROutlinedElevated(text: 'ثبت', onPressed: () {}),
|
||||
ROutlinedElevated(
|
||||
text: 'ثبتwwww',
|
||||
onPressed: () {},
|
||||
backgroundColor: AppColor.blueNormal.disabledColor,
|
||||
pressedBackgroundColor: AppColor.blueNormal,
|
||||
),
|
||||
ROutlinedElevated(text: 'ثبت', onPressed: null),
|
||||
|
||||
RTextButton(text: 'ثبت', onPressed: () {}),
|
||||
RTextButton(text: 'ثبت', onPressed: null),
|
||||
],
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: AppColor.blueNormal),
|
||||
),
|
||||
initialRoute: AppPages.initRoutes,
|
||||
initialBinding: BindingsBuilder.put(() => UserService()),
|
||||
getPages: AppPages.pages,
|
||||
locale: Locale('fa'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,19 +5,24 @@ class Assets {
|
||||
static const String iconsAdd = 'assets/icons/add.svg';
|
||||
static const String iconsArrowLeft = 'assets/icons/arrow_left.svg';
|
||||
static const String iconsArrowRight = 'assets/icons/arrow_right.svg';
|
||||
static const String iconsCall = 'assets/icons/call.svg';
|
||||
static const String iconsDownload = 'assets/icons/download.svg';
|
||||
static const String iconsEdit = 'assets/icons/edit.svg';
|
||||
static const String iconsFilter = 'assets/icons/filter.svg';
|
||||
static const String iconsKey = 'assets/icons/key.svg';
|
||||
static const String iconsScan = 'assets/icons/scan.svg';
|
||||
static const String iconsTrash = 'assets/icons/trash.svg';
|
||||
static const String iconsUser = 'assets/icons/user.svg';
|
||||
static const String imagesInnerSplash = 'assets/images/inner_splash.webp';
|
||||
static const String imagesOutterSplash = 'assets/images/outter_splash.webp';
|
||||
static const String vecAddSvg = 'assets/vec/add.svg.vec';
|
||||
static const String vecArrowLeftSvg = 'assets/vec/arrow_left.svg.vec';
|
||||
static const String vecArrowRightSvg = 'assets/vec/arrow_right.svg.vec';
|
||||
static const String vecCallSvg = 'assets/vec/call.svg.vec';
|
||||
static const String vecDownloadSvg = 'assets/vec/download.svg.vec';
|
||||
static const String vecEditSvg = 'assets/vec/edit.svg.vec';
|
||||
static const String vecFilterSvg = 'assets/vec/filter.svg.vec';
|
||||
static const String vecKeySvg = 'assets/vec/key.svg.vec';
|
||||
static const String vecScanSvg = 'assets/vec/scan.svg.vec';
|
||||
static const String vecTrashSvg = 'assets/vec/trash.svg.vec';
|
||||
|
||||
|
||||
15
lib/presentation/pages/auth/auth_with_otp/logic.dart
Normal file
15
lib/presentation/pages/auth/auth_with_otp/logic.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class AuthWithOtpLogic extends GetxController {
|
||||
@override
|
||||
void onReady() {
|
||||
// TODO: implement onReady
|
||||
super.onReady();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
// TODO: implement onClose
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
15
lib/presentation/pages/auth/auth_with_otp/view.dart
Normal file
15
lib/presentation/pages/auth/auth_with_otp/view.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class AuthWithOtpPage extends StatelessWidget {
|
||||
const AuthWithOtpPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final AuthWithOtpLogic logic = Get.put(AuthWithOtpLogic());
|
||||
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/captcha/captcha_widget.dart';
|
||||
|
||||
class AuthWithUseAndPassLogic extends GetxController {
|
||||
Rx<GlobalKey<FormState>> formKey = GlobalKey<FormState>().obs;
|
||||
Rx<TextEditingController> phoneNumberController = TextEditingController().obs;
|
||||
Rx<TextEditingController> passwordController = TextEditingController().obs;
|
||||
CaptchaController captchaController = CaptchaController();
|
||||
|
||||
RxnString phoneNumber = RxnString(null);
|
||||
RxnString password = RxnString(null);
|
||||
RxBool isOnError = false.obs;
|
||||
RxBool hidePassword = true.obs;
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
// TODO: implement onReady
|
||||
super.onReady();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
// TODO: implement onClose
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
210
lib/presentation/pages/auth/auth_with_use_and_pass/view.dart
Normal file
210
lib/presentation/pages/auth/auth_with_use_and_pass/view.dart
Normal file
@@ -0,0 +1,210 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:rasadyar_app/infrastructure/di/di.dart';
|
||||
import 'package:rasadyar_app/presentation/common/app_color.dart';
|
||||
import 'package:rasadyar_app/presentation/common/app_fonts.dart';
|
||||
import 'package:rasadyar_app/presentation/common/assets.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/buttons/elevated.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/captcha/captcha_widget.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/vec_widget.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class AuthWithUseAndPassPage extends GetView<AuthWithUseAndPassLogic> {
|
||||
AuthWithUseAndPassPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final AuthWithUseAndPassLogic logic = Get.put(AuthWithUseAndPassLogic());
|
||||
return Scaffold(
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [SizedBox(height: 80), logoWidget(), loginForm()],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget loginForm() {
|
||||
return ObxValue((data) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 30, vertical: 50),
|
||||
child: Form(
|
||||
key: data.value,
|
||||
child: Column(
|
||||
children: [
|
||||
ObxValue((phoneController) {
|
||||
return TextFormField(
|
||||
controller: controller.phoneNumberController.value,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
gapPadding: 11,
|
||||
),
|
||||
labelText: 'شماره موبایل',
|
||||
labelStyle: AppFonts.yekan13Regular,
|
||||
errorStyle: AppFonts.yekan13Regular.copyWith(
|
||||
color: AppColor.redNormal,
|
||||
),
|
||||
|
||||
prefixIconConstraints: BoxConstraints(
|
||||
maxHeight: 40,
|
||||
minHeight: 40,
|
||||
maxWidth: 40,
|
||||
minWidth: 40,
|
||||
),
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 8, 6, 8),
|
||||
child: vecWidget(Assets.vecCallSvg),
|
||||
),
|
||||
suffix:
|
||||
phoneController.value.text.trim().isNotEmpty
|
||||
? clearButton(() {
|
||||
phoneController.value.clear();
|
||||
phoneController.refresh();
|
||||
})
|
||||
: null,
|
||||
counterText: '',
|
||||
),
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
decimal: false,
|
||||
signed: false,
|
||||
),
|
||||
|
||||
maxLines: 1,
|
||||
maxLength: 11,
|
||||
onChanged: (value) {
|
||||
if (controller.isOnError.value) {
|
||||
controller.isOnError.value = !controller.isOnError.value;
|
||||
data.value.currentState?.reset();
|
||||
data.refresh();
|
||||
phoneController.value.text = value;
|
||||
}
|
||||
phoneController.refresh();
|
||||
},
|
||||
textInputAction: TextInputAction.next,
|
||||
validator: (value) {
|
||||
if (value == null) {
|
||||
return '⚠️ شماره موبایل را وارد کنید';
|
||||
} else if (value.length < 11) {
|
||||
return '⚠️ شماره موبایل باید 11 رقم باشد';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
style: AppFonts.yekan13Regular,
|
||||
);
|
||||
}, controller.phoneNumberController),
|
||||
|
||||
SizedBox(height: 26),
|
||||
|
||||
ObxValue((passwordController) {
|
||||
return TextFormField(
|
||||
controller: passwordController.value,
|
||||
obscureText: controller.hidePassword.value,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
gapPadding: 11,
|
||||
),
|
||||
labelText: 'رمز عبور',
|
||||
labelStyle: AppFonts.yekan13Regular,
|
||||
errorStyle: AppFonts.yekan13Regular.copyWith(
|
||||
color: AppColor.redNormal,
|
||||
),
|
||||
|
||||
prefixIconConstraints: BoxConstraints(
|
||||
maxHeight: 34,
|
||||
minHeight: 34,
|
||||
maxWidth: 34,
|
||||
minWidth: 34,
|
||||
),
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 8, 8, 8),
|
||||
child: vecWidget(Assets.vecKeySvg),
|
||||
),
|
||||
suffix:
|
||||
passwordController.value.text.trim().isNotEmpty
|
||||
? GestureDetector(
|
||||
onTap: () {
|
||||
controller.hidePassword.value =
|
||||
!controller.hidePassword.value;
|
||||
},
|
||||
child: Icon(
|
||||
controller.hidePassword.value
|
||||
? CupertinoIcons.eye
|
||||
: CupertinoIcons.eye_slash,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
counterText: '',
|
||||
),
|
||||
textInputAction: TextInputAction.done,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
maxLines: 1,
|
||||
onChanged: (value) {
|
||||
if (controller.isOnError.value) {
|
||||
controller.isOnError.value = !controller.isOnError.value;
|
||||
data.value.currentState?.reset();
|
||||
passwordController.value.text = value;
|
||||
}
|
||||
passwordController.refresh();
|
||||
},
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return '⚠️ رمز عبور را وارد کنید'; // "Please enter the password"
|
||||
}
|
||||
return null;
|
||||
},
|
||||
style: AppFonts.yekan13Regular,
|
||||
);
|
||||
}, controller.passwordController),
|
||||
|
||||
SizedBox(height: 26),
|
||||
|
||||
CaptchaWidget(controller: controller.captchaController),
|
||||
|
||||
SizedBox(height: 23),
|
||||
RElevated(
|
||||
text: 'ورود',
|
||||
onPressed: () {
|
||||
di.get<Logger>().t(data.value.currentState?.validate());
|
||||
di.get<Logger>().t(controller.captchaController.validate());
|
||||
if (data.value.currentState?.validate() == true &&
|
||||
controller.captchaController.validate()) {
|
||||
print("==============>ssakldjaskljdklasjd");
|
||||
}
|
||||
},
|
||||
width: Get.width,
|
||||
height: 48,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}, controller.formKey);
|
||||
}
|
||||
|
||||
Widget logoWidget() {
|
||||
return Column(
|
||||
children: [
|
||||
Row(),
|
||||
Image.asset(Assets.imagesInnerSplash, width: 120, height: 120),
|
||||
Text(
|
||||
'سامانه رصدیار',
|
||||
style: AppFonts.yekan16Regular.copyWith(
|
||||
color: AppColor.darkGreyNormal,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget clearButton(VoidCallback onTap) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Icon(CupertinoIcons.multiply_circle, size: 24),
|
||||
);
|
||||
}
|
||||
}
|
||||
67
lib/presentation/pages/splash/logic.dart
Normal file
67
lib/presentation/pages/splash/logic.dart
Normal file
@@ -0,0 +1,67 @@
|
||||
import 'package:flutter/animation.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:rasadyar_app/presentation/routes/app_pages.dart';
|
||||
|
||||
class SplashLogic extends GetxController with GetTickerProviderStateMixin {
|
||||
late final AnimationController scaleController;
|
||||
late final AnimationController rotateController;
|
||||
Rxn<Animation<double>> scaleAnimation = Rxn();
|
||||
Rxn<Animation<double>> rotationAnimation = Rxn();
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
scaleController = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 1500),
|
||||
);
|
||||
|
||||
rotateController = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 8000),
|
||||
);
|
||||
|
||||
scaleAnimation.value = Tween<double>(
|
||||
begin: 0.8,
|
||||
end: 1.2,
|
||||
).animate(scaleController);
|
||||
|
||||
rotationAnimation.value = Tween<double>(
|
||||
begin: 0.0,
|
||||
end: 1,
|
||||
).animate(rotateController);
|
||||
|
||||
rotateController.forward();
|
||||
rotateController.addStatusListener((status) {
|
||||
if (status == AnimationStatus.completed) {
|
||||
rotateController.repeat();
|
||||
} else if (status == AnimationStatus.dismissed) {
|
||||
rotateController.forward();
|
||||
}
|
||||
});
|
||||
|
||||
scaleController.forward();
|
||||
scaleController.addStatusListener((status) {
|
||||
if (status == AnimationStatus.completed) {
|
||||
scaleController.reverse();
|
||||
} else if (status == AnimationStatus.dismissed) {
|
||||
scaleController.forward();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
Get.offAllNamed(AppPaths.authWithUserAndPass);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
rotateController.dispose();
|
||||
scaleController.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
48
lib/presentation/pages/splash/view.dart
Normal file
48
lib/presentation/pages/splash/view.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:rasadyar_app/presentation/common/app_color.dart';
|
||||
import 'package:rasadyar_app/presentation/common/assets.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class SplashPage extends GetView<SplashLogic> {
|
||||
const SplashPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColor.blueDarker,
|
||||
body: Center(
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
ObxValue((data) {
|
||||
return ScaleTransition(
|
||||
scale: data.value!,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 1),
|
||||
child: Image.asset(
|
||||
Assets.imagesInnerSplash,
|
||||
width: 190,
|
||||
height: 190,
|
||||
),
|
||||
),
|
||||
);
|
||||
}, controller.scaleAnimation),
|
||||
|
||||
ObxValue((data) {
|
||||
return RotationTransition(
|
||||
turns: data.value!,
|
||||
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 1),
|
||||
child: Image.asset(Assets.imagesOutterSplash),
|
||||
),
|
||||
);
|
||||
}, controller.rotationAnimation),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
265
lib/presentation/pages/system_design/system_design.dart
Normal file
265
lib/presentation/pages/system_design/system_design.dart
Normal file
@@ -0,0 +1,265 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_app/presentation/common/app_color.dart';
|
||||
import 'package:rasadyar_app/presentation/common/app_fonts.dart';
|
||||
import 'package:rasadyar_app/presentation/utils/color_utils.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/buttons/elevated.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/buttons/fab.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/buttons/fab_outlined.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/buttons/outline_elevated.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/buttons/text_button.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/inputs/r_input.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/pagination/pagination_from_until.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/pagination/show_more.dart';
|
||||
import 'package:rasadyar_app/presentation/widget/tabs/tab.dart';
|
||||
|
||||
class SystemDesignPage extends StatefulWidget {
|
||||
const SystemDesignPage({super.key});
|
||||
|
||||
@override
|
||||
State<SystemDesignPage> createState() => _SystemDesignPageState();
|
||||
}
|
||||
|
||||
class _SystemDesignPageState extends State<SystemDesignPage> {
|
||||
List<bool> _isOpen = [false, false, false, false, false, false];
|
||||
|
||||
void _handleAdd() {
|
||||
print("Add FAB pressed");
|
||||
}
|
||||
|
||||
void _handleEdit() {
|
||||
print("Edit FAB pressed");
|
||||
}
|
||||
|
||||
void _handleDelete() {
|
||||
print("Delete FAB pressed");
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text("System design"), centerTitle: true),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ExpansionPanelList(
|
||||
expansionCallback: (panelIndex, isExpanded) {
|
||||
setState(() {
|
||||
_isOpen[panelIndex] = isExpanded;
|
||||
});
|
||||
},
|
||||
children: [
|
||||
buttonWidget(),
|
||||
fabWidget(),
|
||||
outlinedFabWidget(),
|
||||
paginationWidget(),
|
||||
tabWidget(),
|
||||
inputsWidget(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ExpansionPanel inputsWidget() {
|
||||
return ExpansionPanel(
|
||||
isExpanded: _isOpen[5],
|
||||
headerBuilder: (context, isExpanded) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
"inputs",
|
||||
style: AppFonts.yekan20Regular.copyWith(color: Colors.red),
|
||||
),
|
||||
);
|
||||
},
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
spacing: 14,
|
||||
children: [
|
||||
RTextField(
|
||||
hintText: 'حجم کشتار را در روز به قطعه وارد کنید',
|
||||
hintStyle: AppFonts.yekan13Regular,
|
||||
),
|
||||
RTextField(
|
||||
label: 'تلفن مرغداری',
|
||||
labelStyle: AppFonts.yekan10Regular,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ExpansionPanel tabWidget() {
|
||||
return ExpansionPanel(
|
||||
isExpanded: _isOpen[4],
|
||||
headerBuilder: (context, isExpanded) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
"tab",
|
||||
style: AppFonts.yekan20Regular.copyWith(color: Colors.red),
|
||||
),
|
||||
);
|
||||
},
|
||||
body: Column(
|
||||
spacing: 14,
|
||||
children: [
|
||||
CupertinoSegmentedControlDemo(),
|
||||
CupertinoSegmentedControlDemo2(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ExpansionPanel paginationWidget() {
|
||||
return ExpansionPanel(
|
||||
isExpanded: _isOpen[3],
|
||||
headerBuilder: (context, isExpanded) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
"پیجینیشن",
|
||||
style: AppFonts.yekan20Regular.copyWith(color: Colors.red),
|
||||
),
|
||||
);
|
||||
},
|
||||
body: Column(spacing: 14, children: [RShowMore(), PaginationFromUntil()]),
|
||||
);
|
||||
}
|
||||
|
||||
ExpansionPanel outlinedFabWidget() {
|
||||
return ExpansionPanel(
|
||||
isExpanded: _isOpen[2],
|
||||
headerBuilder: (context, isExpanded) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
"Outlined Fab ",
|
||||
style: AppFonts.yekan20Regular.copyWith(color: Colors.green),
|
||||
),
|
||||
);
|
||||
},
|
||||
body: Column(
|
||||
spacing: 14,
|
||||
children: [
|
||||
Row(),
|
||||
|
||||
RFabOutlined.smallAdd(onPressed: () {}),
|
||||
RFabOutlined.smallAdd(onPressed: null),
|
||||
|
||||
RFabOutlined.smallAddNoBorder(onPressed: () {}),
|
||||
RFabOutlined.smallAddNoBorder(onPressed: null),
|
||||
|
||||
RFabOutlined.add(onPressed: () {}),
|
||||
RFabOutlined.add(onPressed: null),
|
||||
|
||||
RFabOutlined.addNoBorder(onPressed: () {}),
|
||||
RFabOutlined.addNoBorder(onPressed: null),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ExpansionPanel fabWidget() {
|
||||
return ExpansionPanel(
|
||||
isExpanded: _isOpen[1],
|
||||
headerBuilder: (context, isExpanded) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
"Fab",
|
||||
style: AppFonts.yekan20Regular.copyWith(color: Colors.green),
|
||||
),
|
||||
);
|
||||
},
|
||||
body: Column(
|
||||
spacing: 14,
|
||||
children: [
|
||||
Row(),
|
||||
|
||||
RFab.smallAdd(onPressed: () {}),
|
||||
RFab.smallAdd(onPressed: null),
|
||||
|
||||
RFab.add(onPressed: () {}),
|
||||
RFab.add(onPressed: null),
|
||||
|
||||
RFab.smallEdit(onPressed: null),
|
||||
RFab.smallEdit(onPressed: () {}),
|
||||
|
||||
RFab.edit(onPressed: () {}),
|
||||
RFab.edit(onPressed: null),
|
||||
|
||||
RFab.smallDelete(onPressed: () {}),
|
||||
RFab.smallDelete(onPressed: null),
|
||||
|
||||
RFab.delete(onPressed: () {}),
|
||||
RFab.delete(onPressed: null),
|
||||
|
||||
RFab.smallAction(onPressed: () {}),
|
||||
RFab.smallAction(onPressed: null),
|
||||
|
||||
RFab.action(onPressed: () {}),
|
||||
RFab.action(onPressed: null),
|
||||
|
||||
RFab.smallFilter(onPressed: () {}),
|
||||
RFab.smallFilter(onPressed: null),
|
||||
|
||||
RFab.filter(onPressed: () {}),
|
||||
RFab.filter(onPressed: null),
|
||||
|
||||
RFab.smallDownload(onPressed: () {}),
|
||||
RFab.smallDownload(onPressed: null),
|
||||
|
||||
RFab.download(onPressed: () {}),
|
||||
RFab.download(onPressed: null),
|
||||
|
||||
RFab.smallExcel(onPressed: () {}),
|
||||
RFab.smallExcel(onPressed: null),
|
||||
|
||||
RFab.excel(onPressed: () {}),
|
||||
RFab.excel(onPressed: null),
|
||||
|
||||
RFab.smallBack(onPressed: () {}),
|
||||
RFab.smallBack(onPressed: null),
|
||||
|
||||
RFab.back(onPressed: () {}),
|
||||
RFab.back(onPressed: null),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ExpansionPanel buttonWidget() {
|
||||
return ExpansionPanel(
|
||||
isExpanded: _isOpen[0],
|
||||
headerBuilder: (context, isExpanded) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
"دکمه ها",
|
||||
style: AppFonts.yekan20Regular.copyWith(color: Colors.green),
|
||||
),
|
||||
);
|
||||
},
|
||||
body: Column(
|
||||
spacing: 14,
|
||||
children: [
|
||||
Row(),
|
||||
|
||||
RElevated(text: 'ثبت', onPressed: () {}),
|
||||
|
||||
RElevated(text: 'ثبت', onPressed: null),
|
||||
|
||||
ROutlinedElevated(text: 'ثبت', onPressed: () {}),
|
||||
ROutlinedElevated(
|
||||
text: 'ثبتwwww',
|
||||
onPressed: () {},
|
||||
backgroundColor: AppColor.blueNormal.disabledColor,
|
||||
pressedBackgroundColor: AppColor.blueNormal,
|
||||
),
|
||||
ROutlinedElevated(text: 'ثبت', onPressed: null),
|
||||
|
||||
RTextButton(text: 'ثبت', onPressed: () {}),
|
||||
RTextButton(text: 'ثبت', onPressed: null),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
33
lib/presentation/routes/app_pages.dart
Normal file
33
lib/presentation/routes/app_pages.dart
Normal file
@@ -0,0 +1,33 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:rasadyar_app/presentation/pages/auth/auth_with_otp/logic.dart';
|
||||
import 'package:rasadyar_app/presentation/pages/auth/auth_with_otp/view.dart';
|
||||
import 'package:rasadyar_app/presentation/pages/auth/auth_with_use_and_pass/logic.dart';
|
||||
import 'package:rasadyar_app/presentation/pages/auth/auth_with_use_and_pass/view.dart';
|
||||
import 'package:rasadyar_app/presentation/pages/splash/logic.dart';
|
||||
import 'package:rasadyar_app/presentation/pages/splash/view.dart';
|
||||
|
||||
part 'app_paths.dart';
|
||||
|
||||
sealed class AppPages {
|
||||
AppPages._();
|
||||
|
||||
static const String initRoutes = AppPaths.splash;
|
||||
|
||||
static List<GetPage> pages = [
|
||||
GetPage(
|
||||
name: AppPaths.splash,
|
||||
page: () => SplashPage(),
|
||||
binding: BindingsBuilder.put(() => SplashLogic()),
|
||||
),
|
||||
GetPage(
|
||||
name: AppPaths.authWithOtp,
|
||||
page: () => AuthWithOtpPage(),
|
||||
binding: BindingsBuilder.put(() => AuthWithOtpLogic()),
|
||||
),
|
||||
GetPage(
|
||||
name: AppPaths.authWithUserAndPass,
|
||||
page: () => AuthWithUseAndPassPage(),
|
||||
binding: BindingsBuilder.put(() => AuthWithUseAndPassLogic()),
|
||||
),
|
||||
];
|
||||
}
|
||||
9
lib/presentation/routes/app_paths.dart
Normal file
9
lib/presentation/routes/app_paths.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
part of 'app_pages.dart';
|
||||
|
||||
sealed class AppPaths {
|
||||
AppPaths._();
|
||||
|
||||
static const String splash = '/splash';
|
||||
static const String authWithUserAndPass = '/authWithUserAndPass';
|
||||
static const String authWithOtp = '/authWithOtp';
|
||||
}
|
||||
@@ -36,9 +36,7 @@ class _RElevatedState extends State<RElevated> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() {});
|
||||
},
|
||||
onPressed: widget.onPressed,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: widget.backgroundColor ?? AppColor.blueNormal,
|
||||
foregroundColor: widget.foregroundColor ?? Colors.white,
|
||||
|
||||
266
lib/presentation/widget/captcha/captcha_widget.dart
Normal file
266
lib/presentation/widget/captcha/captcha_widget.dart
Normal file
@@ -0,0 +1,266 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_app/presentation/common/app_color.dart';
|
||||
import 'package:rasadyar_app/presentation/common/app_fonts.dart';
|
||||
|
||||
class CaptchaController {
|
||||
int? captchaCode;
|
||||
TextEditingController textController = TextEditingController();
|
||||
GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
|
||||
late Function() refreshCaptcha;
|
||||
|
||||
bool validate() {
|
||||
if (formKey.currentState?.validate() == true) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String get enteredText => textController.text;
|
||||
|
||||
bool isCorrect() {
|
||||
return textController.text == captchaCode.toString();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
textController.clear();
|
||||
}
|
||||
}
|
||||
|
||||
class RandomLinePainter extends CustomPainter {
|
||||
final Random random = Random();
|
||||
final Paint linePaint;
|
||||
final List<Offset> points;
|
||||
|
||||
RandomLinePainter({
|
||||
required this.points,
|
||||
required Color lineColor,
|
||||
double strokeWidth = 2.0,
|
||||
}) : linePaint =
|
||||
Paint()
|
||||
..color = lineColor
|
||||
..strokeWidth = strokeWidth
|
||||
..strokeCap = StrokeCap.round
|
||||
..style = PaintingStyle.stroke;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final path = Path();
|
||||
|
||||
if (points.isNotEmpty) {
|
||||
path.moveTo(points[0].dx, points[0].dy);
|
||||
|
||||
for (int i = 1; i < points.length; i++) {
|
||||
path.lineTo(points[i].dx, points[i].dy);
|
||||
}
|
||||
|
||||
canvas.drawPath(path, linePaint);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(RandomLinePainter oldDelegate) => true;
|
||||
}
|
||||
|
||||
class CaptchaWidget extends StatefulWidget {
|
||||
final CaptchaController controller;
|
||||
final bool autoValidateMode;
|
||||
|
||||
const CaptchaWidget({
|
||||
required this.controller,
|
||||
this.autoValidateMode = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
_CaptchaWidgetState createState() => _CaptchaWidgetState();
|
||||
}
|
||||
|
||||
class _CaptchaWidgetState extends State<CaptchaWidget> {
|
||||
late List<Offset> points;
|
||||
late List<Offset> points1;
|
||||
late List<Offset> points2;
|
||||
bool isOnError = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
generateLines();
|
||||
getRandomSixDigitNumber();
|
||||
|
||||
// Set the refresh function in the controller
|
||||
widget.controller.refreshCaptcha = () {
|
||||
getRandomSixDigitNumber();
|
||||
generateLines();
|
||||
setState(() {});
|
||||
};
|
||||
}
|
||||
|
||||
void generateLines() {
|
||||
points = generateRandomLine();
|
||||
points1 = generateRandomLine();
|
||||
points2 = generateRandomLine();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
List<Offset> generateRandomLine() {
|
||||
final random = Random();
|
||||
int pointCount = random.nextInt(10) + 5;
|
||||
List<Offset> points = [];
|
||||
|
||||
double previousY = 0;
|
||||
|
||||
for (int i = 0; i < pointCount; i++) {
|
||||
double x = (i / (pointCount - 1)) * 135;
|
||||
|
||||
if (i == 0) {
|
||||
previousY = 24;
|
||||
} else {
|
||||
double change = (random.nextDouble() * 20) - 10;
|
||||
previousY = max(5, min(43, previousY + change));
|
||||
}
|
||||
|
||||
points.add(Offset(x, previousY));
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
void getRandomSixDigitNumber() {
|
||||
final random = Random();
|
||||
widget.controller.captchaCode = random.nextInt(900000) + 100000;
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 135,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.whiteNormalHover,
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Stack(
|
||||
alignment: AlignmentDirectional.center,
|
||||
children: [
|
||||
CustomPaint(
|
||||
painter: RandomLinePainter(
|
||||
points: points,
|
||||
lineColor: Colors.blue,
|
||||
strokeWidth: 1.0,
|
||||
),
|
||||
size: const Size(double.infinity, double.infinity),
|
||||
),
|
||||
CustomPaint(
|
||||
painter: RandomLinePainter(
|
||||
points: points1,
|
||||
lineColor: Colors.green,
|
||||
strokeWidth: 1.0,
|
||||
),
|
||||
size: const Size(double.infinity, double.infinity),
|
||||
),
|
||||
CustomPaint(
|
||||
painter: RandomLinePainter(
|
||||
points: points2,
|
||||
lineColor: Colors.red,
|
||||
strokeWidth: 1.0,
|
||||
),
|
||||
size: const Size(double.infinity, double.infinity),
|
||||
),
|
||||
Text(
|
||||
widget.controller.captchaCode.toString(),
|
||||
style: AppFonts.yekan24Regular,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: widget.controller.refreshCaptcha,
|
||||
icon: Icon(CupertinoIcons.refresh, size: 16),
|
||||
),
|
||||
Expanded(
|
||||
child: Form(
|
||||
key: widget.controller.formKey,
|
||||
autovalidateMode:
|
||||
widget.autoValidateMode
|
||||
? AutovalidateMode.onUserInteraction
|
||||
: AutovalidateMode.disabled,
|
||||
child: TextFormField(
|
||||
controller: widget.controller.textController,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
gapPadding: 11,
|
||||
),
|
||||
labelText: 'کد امنیتی',
|
||||
labelStyle: AppFonts.yekan13Regular,
|
||||
errorStyle: AppFonts.yekan10Regular.copyWith(
|
||||
color: AppColor.redNormal,
|
||||
fontSize: 8,
|
||||
),
|
||||
suffixIconConstraints: BoxConstraints(
|
||||
maxHeight: 24,
|
||||
minHeight: 24,
|
||||
maxWidth: 24,
|
||||
minWidth: 24,
|
||||
),
|
||||
|
||||
suffix:
|
||||
widget.controller.textController.text.trim().isNotEmpty
|
||||
? clearButton(() {
|
||||
widget.controller.textController.clear();
|
||||
setState(() {});
|
||||
})
|
||||
: null,
|
||||
counterText: '',
|
||||
),
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
decimal: false,
|
||||
signed: false,
|
||||
),
|
||||
maxLines: 1,
|
||||
maxLength: 6,
|
||||
onChanged: (value) {
|
||||
if (isOnError) {
|
||||
isOnError = !isOnError;
|
||||
widget.controller.formKey.currentState?.reset();
|
||||
widget.controller.textController.text = value;
|
||||
}
|
||||
setState(() {});
|
||||
},
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
isOnError = true;
|
||||
return 'کد امنیتی را وارد کنید';
|
||||
}
|
||||
if (value != widget.controller.captchaCode.toString()) {
|
||||
isOnError = true;
|
||||
return '⚠️کد امنیتی وارد شده اشتباه است';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
style: AppFonts.yekan13Regular,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget clearButton(VoidCallback onTap) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Icon(CupertinoIcons.multiply_circle, size: 18),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,25 @@ SvgPicture vecWidget(
|
||||
color != null ? ColorFilter.mode(color, BlendMode.srcIn) : null,
|
||||
);
|
||||
}
|
||||
|
||||
SvgPicture svgWidget(
|
||||
String assets, {
|
||||
double? width,
|
||||
double? height,
|
||||
BoxFit? fit,
|
||||
Color? color,
|
||||
}) {
|
||||
return SvgPicture.asset(
|
||||
assets,
|
||||
width: width,
|
||||
height: height,
|
||||
fit: fit ?? BoxFit.contain,
|
||||
colorFilter:
|
||||
color != null ? ColorFilter.mode(color, BlendMode.srcIn) : null,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Widget vecWidget2(
|
||||
String assets, {
|
||||
double? width,
|
||||
|
||||
@@ -21,10 +21,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
|
||||
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.0"
|
||||
version: "2.7.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -247,10 +247,10 @@ packages:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: freezed
|
||||
sha256: a6274c34d61b3d68082f2b0e9a641a3ec197e525d269f35b82f62d5b2c6d9f75
|
||||
sha256: "6022db4c7bfa626841b2a10f34dd1e1b68e8f8f9650db6112dcdeeca45ca793c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
version: "3.0.6"
|
||||
freezed_annotation:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -54,7 +54,6 @@ dev_dependencies:
|
||||
|
||||
|
||||
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
|
||||
Reference in New Issue
Block a user