feat: login page init

This commit is contained in:
louis 2024-03-18 15:54:06 +08:00
parent 9abc9c0f2b
commit 78628e2242
12 changed files with 392 additions and 193 deletions

BIN
assets/images/bg copy.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
assets/images/bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -1,40 +1,38 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart'; import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class AppTheme { class AppTheme {
AppTheme._(); AppTheme._();
static const Color primaryColor = Color(0xFFB0F320); static const Color primaryColor = Color(0xFFC89607);
static const Color primaryColorDark = Color(0xFFABEE1C); static Color primaryColorLight = primaryColor.withOpacity(0.7);
static const Color nearlyWhite = Color(0xFFFEFEFE); static Color primaryColorDark = Color.fromRGBO(
primaryColor.red,
primaryColor.green,
primaryColor.blue,
1 - primaryColor.computeLuminance(),
);
static const Color white = Color(0xFFFFFFFF); static const Color white = Color(0xFFFFFFFF);
static const Color nearlyWhite = Color(0xFFFEFEFE);
static const Color black = Color(0xFF000000);
static const Color nearlyBlack = Color(0xFF213333); static const Color nearlyBlack = Color(0xFF213333);
static const Color grey = Color(0xFF3A5160); static const Color grey = Color(0xFF3A5160);
static const Color dark_grey = Color(0xFF313A44);
static const Color snackbarErrorBackgroudColor = Colors.red; static const Color snackbarErrorBackgroudColor = Colors.red;
static const Color snackbarSuccessBackgroudColor = Colors.green; static const Color snackbarSuccessBackgroudColor = Colors.green;
static const Color snackbarWarningBackgroudColor = Colors.orange; static const Color snackbarWarningBackgroudColor = Colors.orange;
static const Color roleIconBackgroudColor = Color(0xff002f5f);
static const Color fixturePublishStatusColor = Color(0xFF5B8C00);
static const Color fixtureNotPublishStatusColor = Color(0xFFD7616E);
static const Color darkText = Color(0xFF253840);
static const Color darkerText = Color(0xFF17262A);
static const Color lightText = Color(0xFF4A6572);
static const Color dismissibleBackground = Color(0xFF364A54); static const Color dismissibleBackground = Color(0xFF364A54);
static const String fontName = 'NotoSans'; static const String fontName = 'NotoSans';
} }
final theme = ThemeData( final theme = ThemeData(
fontFamily: AppTheme.fontName, fontFamily: AppTheme.fontName,
// primarySwatch:MaterialColor() AppTheme.primaryColor,
visualDensity: VisualDensity.adaptivePlatformDensity, visualDensity: VisualDensity.adaptivePlatformDensity,
primaryColor: AppTheme.primaryColor, primaryColor: AppTheme.primaryColor,
primaryColorDark: AppTheme.primaryColorDark, primaryColorDark: AppTheme.primaryColorDark,
progressIndicatorTheme: progressIndicatorTheme:
ProgressIndicatorThemeData(color: AppTheme.primaryColor), const ProgressIndicatorThemeData(color: AppTheme.primaryColor),
dividerColor: Color(0xFFDBDBDA), dividerColor: AppTheme.grey,
cardColor: Colors.white, cardColor: AppTheme.white,
scaffoldBackgroundColor: Color(0xFFF8F8F8), scaffoldBackgroundColor: AppTheme.nearlyWhite,
tabBarTheme: TabBarTheme( tabBarTheme: TabBarTheme(
indicator: BoxDecoration( indicator: BoxDecoration(
border: border:
@ -42,11 +40,27 @@ final theme = ThemeData(
)), )),
appBarTheme: AppBarTheme( appBarTheme: AppBarTheme(
centerTitle: true, centerTitle: true,
iconTheme: IconThemeData(color: Colors.black), iconTheme: const IconThemeData(color: Colors.black),
backgroundColor: AppTheme.primaryColor, backgroundColor: AppTheme.primaryColor,
titleTextStyle: TextStyle( titleTextStyle: TextStyle(
color: Colors.black, color: Colors.black,
fontSize: ScreenAdaper.sp(20), fontSize: ScreenAdaper.sp(20),
fontWeight: FontWeight.bold), fontWeight: FontWeight.bold),
), ),
inputDecorationTheme: InputDecorationTheme(
floatingLabelStyle: TextStyle(
color: AppTheme.primaryColor,
fontSize: ScreenAdaper.sp(30),
),
labelStyle: TextStyle(
fontSize: ScreenAdaper.sp(25),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15))),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15)),
borderSide: BorderSide(
color: AppTheme.primaryColorLight, width: 2), //
),
),
); );

View File

@ -14,7 +14,10 @@ class Global {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
/// ///
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); // await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
///
await SystemChrome.setPreferredOrientations(DeviceOrientation.values);
/// UI bug先隐藏功能 /// UI bug先隐藏功能
setSystemUi(); setSystemUi();

View File

@ -16,9 +16,13 @@ class IndexPage extends StatelessWidget {
const IndexPage({super.key}); const IndexPage({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isLandscape =
MediaQuery.of(context).orientation == Orientation.landscape;
return ScreenUtilInit( return ScreenUtilInit(
minTextAdapt: true, minTextAdapt: true,
designSize: const Size(932, 1490), // 2800 * 1800p 309ppi designSize: isLandscape
? const Size(1490, 932)
: const Size(932, 1490), // 2800 * 1800p 309ppi
builder: (buildContext, child) => RefreshConfiguration( builder: (buildContext, child) => RefreshConfiguration(
headerBuilder: () => RefreshHeader(), headerBuilder: () => RefreshHeader(),
footerBuilder: () => const RefreshFooter(), footerBuilder: () => const RefreshFooter(),
@ -33,20 +37,13 @@ class IndexPage extends StatelessWidget {
initialRoute: getInitialRoute(), initialRoute: getInitialRoute(),
// initialRoute: RouteConfig.onboarding, // initialRoute: RouteConfig.onboarding,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
builder: (_, widget) => Obx(() => MediaQuery( builder: (_, widget) {
return Obx(() => MediaQuery(
// //
data: MediaQuery.of(context).copyWith( data: MediaQuery.of(context).copyWith(
textScaleFactor: AppInfoService.to.scale.value), textScaleFactor: AppInfoService.to.scale.value),
child: GestureDetector( child: widget!));
onTap: () { })));
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus &&
currentFocus.focusedChild != null) {
FocusManager.instance.primaryFocus?.unfocus();
}
},
child: widget!,
))))));
} }
String getInitialRoute() { String getInitialRoute() {

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:sk_base_mobile/global.dart'; import 'package:sk_base_mobile/global.dart';
import 'package:sk_base_mobile/index.dart'; import 'package:sk_base_mobile/index.dart';
import 'package:sk_base_mobile/util/logger_util.dart'; import 'package:sk_base_mobile/util/logger_util.dart';
@ -9,6 +10,5 @@ Future<void> main() async {
} catch (e) { } catch (e) {
LoggerUtil().error('Init failed, please try again.${e}'); LoggerUtil().error('Init failed, please try again.${e}');
} }
runApp(const IndexPage()); runApp(const IndexPage());
} }

View File

@ -0,0 +1,194 @@
import 'package:date_format/date_format.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/screens/login/login.controller.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class LoginScreen extends StatelessWidget {
LoginScreen({super.key});
final _controller = Get.put<LoginController>(LoginController());
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/bg.jpg'),
fit: BoxFit.cover,
),
),
child: buildBody()));
// return Scaffold(
// body: SafeArea(child: buildBody()));
}
buildBody() {
return Center(
child: SingleChildScrollView(
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
//
FocusScope.of(Get.context!).unfocus();
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image(
height: ScreenAdaper.width(150),
image: const AssetImage(
'assets/images/company_logo.png',
)),
SizedBox(
width: ScreenAdaper.width(10),
),
Text(
'山矿通',
style: TextStyle(
fontWeight: FontWeight.w700,
color: AppTheme.nearlyWhite,
letterSpacing: ScreenAdaper.sp(5),
fontSize: ScreenAdaper.sp(70)),
)
],
),
// Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Text(
// '山矿通',
// style: TextStyle(
// letterSpacing: ScreenAdaper.sp(10),
// fontSize: ScreenAdaper.sp(70)),
// )
// ],
// ),
SizedBox(height: ScreenAdaper.height(50)),
buildForm(),
SizedBox(height: ScreenAdaper.height(50)),
],
),
),
));
}
Widget buildForm() {
final children = [
buildUserNameInput(),
SizedBox(
height: ScreenAdaper.height(20),
),
buildPasswordInput(),
SizedBox(
height: ScreenAdaper.height(20),
),
buildForgotPassword(),
SizedBox(
height: ScreenAdaper.height(20),
),
buildSubmitButton(),
];
final child = Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: children,
);
return Form(
key: _controller.formKey,
child: Container(
alignment: Alignment.center,
width: ScreenAdaper.width(800),
padding: EdgeInsets.symmetric(
vertical: 0, horizontal: ScreenAdaper.width(50)),
child: child,
));
}
Widget buildSubmitButton() {
final button = ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(ScreenAdaper.sp(30))),
padding: EdgeInsets.symmetric(
horizontal: ScreenAdaper.height(25),
vertical: ScreenAdaper.height(20)),
foregroundColor: AppTheme.nearlyWhite,
textStyle: TextStyle(
letterSpacing: 5,
fontWeight: FontWeight.bold,
fontSize: ScreenAdaper.sp(25)),
backgroundColor: AppTheme.primaryColor),
onPressed: _controller.doLogin,
child: const Text('登录'),
);
final child = Expanded(
child: button,
);
return Row(
children: [child],
);
}
Widget buildUserNameInput() {
return TextFormField(
decoration: const InputDecoration(
labelText: '用户名',
),
onFieldSubmitted: (value) {
_controller.passwordFocusNode.requestFocus();
},
style: TextStyle(fontSize: ScreenAdaper.sp(25)),
onChanged: (value) {},
validator: (String? value) {
if (value == null || value == '') {
return '用户名必填';
} else {
return null;
}
},
);
}
Widget buildPasswordInput() {
return TextFormField(
decoration: const InputDecoration(
labelText: '密码',
),
obscureText: true,
focusNode: _controller.passwordFocusNode,
onFieldSubmitted: (value) {
_controller.doLogin();
},
style: TextStyle(fontSize: ScreenAdaper.sp(25)),
onChanged: (value) {},
validator: (String? value) {
return (value ?? '').length >= 6 ? null : '密码长度至少6位';
},
);
}
Widget buildForgotPassword() {
return GestureDetector(
onTap: () {},
child: Container(
alignment: Alignment.centerRight,
child: Text(
'忘记密码?',
style: TextStyle(
color: Colors.grey,
fontSize: ScreenAdaper.sp(20),
decoration: TextDecoration.underline,
),
)),
);
}
}

View File

@ -7,31 +7,12 @@ import '../../util/util.dart';
class LoginController extends GetxController { class LoginController extends GetxController {
final isAgreeTerm = RxBool(false); final isAgreeTerm = RxBool(false);
final formKey = GlobalKey<FormState>();
Future<void> fastLogin() async { final passwordFocusNode = FocusNode();
await confirmAgreement().then((value) async { bool loading = false;
if (value != null) { Future<void> doLogin() async {
await AuthStore.to.login(LoginEnum.fastLogin); if (!formKey.currentState!.validate()) {
} return;
}); }
}
Future confirmAgreement() async {
// if (!isAgreeTerm.value) {
// return Get.defaultDialog(
// title: '',
// content: ConfirmAgreement(),
// titlePadding: const EdgeInsets.all(0));
// } else {
// return Future(() => true);
// }
}
Future<void> onPrivacyPolicyTap() async {
// launchUrl(Uri.parse(GloablConfig.PRIVACY_POLICY));
}
Future<void> onTermTap() async {
// launchUrl(Uri.parse(GloablConfig.TERM_OF_USER));
} }
} }

View File

@ -1,195 +1,204 @@
import 'package:date_format/date_format.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/app_theme.dart'; import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/screens/login/login.controller.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart'; import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class LoginScreen extends StatelessWidget { class LoginScreen extends StatelessWidget {
LoginScreen({super.key}); LoginScreen({super.key});
final formKey = GlobalKey<FormState>(); final _controller = Get.put<LoginController>(LoginController());
late BuildContext theContext; double formWidth = 700;
bool loading = false;
String? username;
String? password;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold(body: SafeArea(child: buildBody())); return Scaffold(
body: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/bg.jpg'),
fit: BoxFit.cover,
),
),
child: buildBody()));
} }
buildBody() { Widget buildBody() {
return GestureDetector( return GestureDetector(
behavior: HitTestBehavior.translucent, behavior: HitTestBehavior.translucent,
onTap: () { onTap: () {
// //
FocusScope.of(Get.context!).unfocus();
}, },
child: Padding(
padding: EdgeInsets.fromLTRB(0, 0, 10, 30),
child: Center( child: Center(
child: SingleChildScrollView(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Expanded( Row(
child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
// Logo(size: 120), Image(
SizedBox(height: 30), height: ScreenAdaper.width(150),
image: const AssetImage(
'assets/images/company_logo.png',
)),
SizedBox(
width: ScreenAdaper.width(10),
),
Text(
'山矿通',
style: TextStyle(
fontWeight: FontWeight.w700,
color: AppTheme.nearlyWhite,
letterSpacing: ScreenAdaper.sp(5),
fontSize: ScreenAdaper.sp(70)),
)
],
),
SizedBox(height: ScreenAdaper.height(50)),
buildForm(), buildForm(),
SizedBox(height: 30), SizedBox(height: ScreenAdaper.height(50)),
buildSubmitButton(),
SizedBox(height: ScreenAdaper.height(50)),
], ],
), ),
), ),
buildGoRegister(), ));
buildNotice(),
],
),
)));
}
Widget buildNotice() {
return Padding(
padding: EdgeInsets.fromLTRB(50, 5, 50, 5),
child: Text(
'If you have registered as a PLAYER, COACH, MANAGER or REFEREE, please use your login details provided.',
textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.bold)));
} }
Widget buildForm() { Widget buildForm() {
final children = [ final children = [
buildUserNameInput(), buildUserNameInput(),
SizedBox( Divider(
height: 15, thickness: ScreenAdaper.height(2),
), ),
buildPasswordInput(), buildPasswordInput(),
SizedBox(
height: 12,
),
buildForgotPassword(),
SizedBox(
height: 12,
),
buildSubmitButton(),
/* SizedBox(
height: 12,
),
buildRegistry() */
]; ];
final child = Column( final child = Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: children, children: children,
); );
return Form( return Form(
key: formKey, key: _controller.formKey,
child: Padding( child: Container(
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 50), padding: EdgeInsets.symmetric(vertical: ScreenAdaper.height(10)),
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: AppTheme.black.withOpacity(0.3),
spreadRadius: ScreenAdaper.sp(10),
blurRadius: ScreenAdaper.sp(10),
offset: Offset(
0, ScreenAdaper.height(2)), // changes position of shadow
),
],
border: Border.all(width: 0),
borderRadius: BorderRadius.circular(ScreenAdaper.sp(30)),
color: AppTheme.nearlyWhite.withOpacity(0.9),
),
alignment: Alignment.center,
width: ScreenAdaper.width(formWidth),
child: child, child: child,
)); ));
} }
Widget buildGoRegister() => Container(
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Text('Don\'t have an account?'),
SizedBox(width: 5),
GestureDetector(
onTap: () {},
child: Text('Create',
style: TextStyle(
color: AppTheme.primaryColor,
decoration: TextDecoration.underline,
))),
]),
);
Widget buildSubmitButton() { Widget buildSubmitButton() {
final color = AppTheme.primaryColor; final button = ElevatedButton(
style: ElevatedButton.styleFrom(
// final button = RaisedButton( shape: RoundedRectangleBorder(
// shape: StadiumBorder(), borderRadius: BorderRadius.circular(ScreenAdaper.sp(30))),
// child: loading padding: EdgeInsets.symmetric(
// ? SizedBox( horizontal: ScreenAdaper.height(25),
// child: CircularProgressIndicator( vertical: ScreenAdaper.height(20)),
// color: AppTheme.primaryColorDark, foregroundColor: AppTheme.nearlyWhite,
// ), textStyle: TextStyle(
// height: 18, letterSpacing: 5,
// width: 18, fontWeight: FontWeight.bold,
// ) fontSize: ScreenAdaper.sp(25)),
// : Text('Sign In'), backgroundColor: AppTheme.primaryColor),
// onPressed: () { onPressed: _controller.doLogin,
// _submit(); child: const Text('登录'),
// }, );
// color: color, final child = Expanded(
// /* elevation: 20, */ child: button,
// padding: EdgeInsets.all(12), );
// ); return Container(
decoration: BoxDecoration(
// final child = Expanded( border: Border.all(width: 0),
// child: button, borderRadius: BorderRadius.circular(ScreenAdaper.sp(30)),
// ); boxShadow: [
BoxShadow(
return Row( color: AppTheme.black.withOpacity(0.3),
children: [], spreadRadius: ScreenAdaper.sp(10),
blurRadius: ScreenAdaper.sp(10),
offset:
Offset(0, ScreenAdaper.height(5)), // changes position of shadow
),
],
),
alignment: Alignment.center,
width: ScreenAdaper.width(formWidth),
child: Row(
children: [child],
),
); );
} }
Widget buildUserNameInput() { Widget buildUserNameInput() {
return TextFormField( return TextFormField(
initialValue: username,
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Email / Reg ID', prefixIcon: Icon(
border: OutlineInputBorder( Icons.person_2_outlined,
borderRadius: BorderRadius.circular(ScreenAdaper.sp(10))), size: ScreenAdaper.sp(40),
fillColor: Color(0xFFF8F8F8),
), ),
onChanged: (value) { hintText: '用户名',
this.username = value; border: InputBorder.none,
}, focusedBorder: InputBorder.none),
validator: (String? value) { onFieldSubmitted: (value) {
if (value == null || value == '') { _controller.passwordFocusNode.requestFocus();
return 'Email / Reg ID is required';
} else {
return null;
}
}, },
style: TextStyle(fontSize: ScreenAdaper.sp(25)),
onChanged: (value) {},
); );
} }
Widget buildPasswordInput() { Widget buildPasswordInput() {
return TextFormField( return TextFormField(
initialValue: password,
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Password', prefixIcon: Icon(
/* border: OutlineInputBorder(), */ Icons.lock_outlined,
fillColor: Color(0xFFF8F8F8), size: ScreenAdaper.sp(40),
), ),
hintText: '密码',
border: InputBorder.none,
focusedBorder: InputBorder.none),
obscureText: true, obscureText: true,
onChanged: (value) { focusNode: _controller.passwordFocusNode,
this.password = value; onFieldSubmitted: (value) {
}, _controller.doLogin();
validator: (String? value) {
return (value ?? '').length >= 6 ? null : 'Invalid password';
}, },
style: TextStyle(fontSize: ScreenAdaper.sp(25)),
onChanged: (value) {},
// validator: (String? value) {
// return (value ?? '').length >= 6 ? null : '密码长度至少6位';
// },
); );
} }
Future<void> _submit() async {
if (!formKey.currentState!.validate()) {
return;
}
return;
}
Widget buildForgotPassword() { Widget buildForgotPassword() {
return GestureDetector( return GestureDetector(
onTap: () {}, onTap: () {},
child: Container( child: Container(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: Text( child: Text(
'Forgot Password?', '忘记密码?',
style: TextStyle( style: TextStyle(
color: Colors.grey, color: Colors.grey,
fontSize: ScreenAdaper.sp(20),
decoration: TextDecoration.underline, decoration: TextDecoration.underline,
), ),
)), )),

View File

@ -1,3 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@ -45,6 +46,6 @@ class ScreenAdaper {
} }
static isLandspace() { static isLandspace() {
return Get.width > Get.height; return Get.context?.orientation == Orientation.landscape;
} }
} }