mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
config sign in screen
This commit is contained in:
parent
d30ee5db89
commit
084f939ed0
6
app_flowy/.vscode/tasks.json
vendored
6
app_flowy/.vscode/tasks.json
vendored
@ -18,9 +18,9 @@
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/../"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$rustc"
|
||||
],
|
||||
// "problemMatcher": [
|
||||
// "$rustc"
|
||||
// ],
|
||||
"label": "BuildRust"
|
||||
}
|
||||
]
|
||||
|
@ -15,7 +15,7 @@ analyzer:
|
||||
- "**/*.g.dart"
|
||||
- "**/*.freezed.dart"
|
||||
- "packages/flowy_editor/**"
|
||||
- "packages/flowy_infra_ui/**"
|
||||
# - "packages/flowy_infra_ui/**"
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
|
BIN
app_flowy/assets/images/app_flowy_logo.jpg
Normal file
BIN
app_flowy/assets/images/app_flowy_logo.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
@ -1,8 +1,15 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/user/application/sign_in/sign_in_bloc.dart';
|
||||
import 'package:app_flowy/user/presentation/sign_in/widgets/body.dart';
|
||||
import 'package:app_flowy/user/presentation/sign_in/widgets/background.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_screen.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-user/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-user/user_detail.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
class SignInScreen extends StatelessWidget {
|
||||
const SignInScreen({Key? key}) : super(key: key);
|
||||
@ -11,9 +18,129 @@ class SignInScreen extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<SignInBloc>(),
|
||||
child: const Scaffold(
|
||||
body: Body(),
|
||||
child: Scaffold(
|
||||
body: BlocProvider(
|
||||
create: (context) => getIt<SignInBloc>(),
|
||||
child: BlocConsumer<SignInBloc, SignInState>(
|
||||
listenWhen: (p, c) => p != c,
|
||||
listener: (context, state) {
|
||||
state.signInFailure.fold(
|
||||
() {},
|
||||
(result) => _handleStateErrors(result, context),
|
||||
);
|
||||
},
|
||||
builder: (context, state) => const SignInForm(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleStateErrors(
|
||||
Either<UserDetail, UserError> some, BuildContext context) {
|
||||
some.fold(
|
||||
(userDetail) => _showHomeScreen(context, userDetail),
|
||||
(result) => _showErrorMessage(context, result.msg),
|
||||
);
|
||||
}
|
||||
|
||||
void _showErrorMessage(BuildContext context, String msg) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(msg),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showHomeScreen(BuildContext context, UserDetail userDetail) {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return HomeScreen(userDetail);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SignInForm extends StatelessWidget {
|
||||
const SignInForm({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Align(
|
||||
alignment: Alignment.center,
|
||||
child: SignInFormContainer(
|
||||
children: [
|
||||
const SignInTitle(
|
||||
title: 'Login to Appflowy',
|
||||
logoSize: Size(60, 60),
|
||||
),
|
||||
const VSpace(30),
|
||||
RoundedInputField(
|
||||
hintText: 'email',
|
||||
onChanged: (value) =>
|
||||
context.read<SignInBloc>().add(SignInEvent.emailChanged(value)),
|
||||
),
|
||||
RoundedInputField(
|
||||
obscureText: true,
|
||||
hintText: 'password',
|
||||
onChanged: (value) => context
|
||||
.read<SignInBloc>()
|
||||
.add(SignInEvent.passwordChanged(value)),
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
textStyle: const TextStyle(fontSize: 12),
|
||||
),
|
||||
onPressed: () => _showForgetPasswordScreen(context),
|
||||
child: const Text(
|
||||
'Forgot Password?',
|
||||
style: TextStyle(color: Colors.lightBlue),
|
||||
),
|
||||
),
|
||||
RoundedButton(
|
||||
title: 'Login',
|
||||
height: 60,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: Colors.lightBlue,
|
||||
press: () {
|
||||
context
|
||||
.read<SignInBloc>()
|
||||
.add(const SignInEvent.signedInWithUserEmailAndPassword());
|
||||
},
|
||||
),
|
||||
const VSpace(10),
|
||||
Row(
|
||||
children: [
|
||||
const Text("Dont't have an account",
|
||||
style: TextStyle(color: Colors.blueGrey, fontSize: 12)),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
textStyle: const TextStyle(fontSize: 12),
|
||||
),
|
||||
onPressed: () {},
|
||||
child: const Text(
|
||||
'Sign Up',
|
||||
style: TextStyle(color: Colors.lightBlue),
|
||||
),
|
||||
),
|
||||
],
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
),
|
||||
if (context.read<SignInBloc>().state.isSubmitting) ...[
|
||||
const SizedBox(height: 8),
|
||||
const LinearProgressIndicator(value: null),
|
||||
]
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showForgetPasswordScreen(BuildContext context) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +1,57 @@
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SignInBackground extends StatelessWidget {
|
||||
final Widget child;
|
||||
const SignInBackground({
|
||||
class SignInFormContainer extends StatelessWidget {
|
||||
final List<Widget> children;
|
||||
const SignInFormContainer({
|
||||
Key? key,
|
||||
required this.child,
|
||||
required this.children,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var size = MediaQuery.of(context).size;
|
||||
final size = MediaQuery.of(context).size;
|
||||
return SizedBox(
|
||||
height: size.height,
|
||||
width: double.infinity,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Image(
|
||||
fit: BoxFit.cover,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
image: const AssetImage(
|
||||
'assets/images/appflowy_launch_splash.jpg')),
|
||||
child,
|
||||
],
|
||||
));
|
||||
width: size.width * 0.3,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: children,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SignInTitle extends StatelessWidget {
|
||||
final String title;
|
||||
final Size logoSize;
|
||||
const SignInTitle({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.logoSize,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image(
|
||||
fit: BoxFit.cover,
|
||||
width: logoSize.width,
|
||||
height: logoSize.height,
|
||||
image: const AssetImage('assets/images/app_flowy_logo.jpg')),
|
||||
const VSpace(30),
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 20,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,125 +0,0 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/user/application/sign_in/sign_in_bloc.dart';
|
||||
import 'package:app_flowy/user/presentation/sign_in/widgets/background.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_screen.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class Body extends StatelessWidget {
|
||||
const Body({Key? key}) : super(key: key);
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<SignInBloc>(),
|
||||
child: const SignInBackground(
|
||||
child: SignInForm(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SignInForm extends StatelessWidget {
|
||||
const SignInForm({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocConsumer<SignInBloc, SignInState>(
|
||||
listenWhen: (p, c) => p != c,
|
||||
listener: (context, state) {
|
||||
state.signInFailure.fold(
|
||||
() {},
|
||||
(result) => _handleStateErrors(result, context),
|
||||
);
|
||||
},
|
||||
builder: (context, state) {
|
||||
return SignInFormBackground(
|
||||
children: [
|
||||
const SizedBox(height: 30),
|
||||
RoundedInputField(
|
||||
icon: Icons.person,
|
||||
hintText: 'email',
|
||||
onChanged: (value) => context
|
||||
.read<SignInBloc>()
|
||||
.add(SignInEvent.emailChanged(value)),
|
||||
),
|
||||
RoundedInputField(
|
||||
icon: Icons.lock,
|
||||
obscureText: true,
|
||||
hintText: 'password',
|
||||
onChanged: (value) => context
|
||||
.read<SignInBloc>()
|
||||
.add(SignInEvent.passwordChanged(value)),
|
||||
),
|
||||
RoundedButton(
|
||||
title: 'LOGIN',
|
||||
press: () {
|
||||
context
|
||||
.read<SignInBloc>()
|
||||
.add(const SignInEvent.signedInWithUserEmailAndPassword());
|
||||
},
|
||||
),
|
||||
if (state.isSubmitting) ...[
|
||||
const SizedBox(height: 8),
|
||||
const LinearProgressIndicator(value: null),
|
||||
]
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _handleStateErrors(
|
||||
Either<UserDetail, UserError> some, BuildContext context) {
|
||||
some.fold(
|
||||
(userDetail) => showHomeScreen(context, userDetail),
|
||||
(result) => _showErrorMessage(context, result.msg),
|
||||
);
|
||||
}
|
||||
|
||||
void _showErrorMessage(BuildContext context, String msg) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(msg),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void showHomeScreen(BuildContext context, UserDetail userDetail) {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return HomeScreen(userDetail);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SignInFormBackground extends StatelessWidget {
|
||||
final List<Widget> children;
|
||||
const SignInFormBackground({
|
||||
Key? key,
|
||||
required this.children,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final size = MediaQuery.of(context).size;
|
||||
|
||||
return Container(
|
||||
width: size.width * 0.4,
|
||||
alignment: Alignment.center,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center, children: children),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -3,13 +3,23 @@ import 'package:flutter/material.dart';
|
||||
class RoundedButton extends StatelessWidget {
|
||||
final VoidCallback? press;
|
||||
final String? title;
|
||||
final Size? size;
|
||||
final double? width;
|
||||
final double? height;
|
||||
final BorderRadius borderRadius;
|
||||
final Color borderColor;
|
||||
final Color color;
|
||||
final Color textColor;
|
||||
|
||||
const RoundedButton({
|
||||
Key? key,
|
||||
this.press,
|
||||
this.title,
|
||||
this.size,
|
||||
this.width,
|
||||
this.height,
|
||||
this.borderRadius = BorderRadius.zero,
|
||||
this.borderColor = Colors.transparent,
|
||||
this.color = Colors.transparent,
|
||||
this.textColor = Colors.white,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -17,15 +27,22 @@ class RoundedButton extends StatelessWidget {
|
||||
return ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minWidth: 100,
|
||||
maxWidth: size?.width ?? double.infinity,
|
||||
maxWidth: width ?? double.infinity,
|
||||
minHeight: 50,
|
||||
maxHeight: size?.height ?? double.infinity,
|
||||
maxHeight: height ?? 60,
|
||||
),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: TextButton(
|
||||
child: Text(title ?? ''),
|
||||
onPressed: press,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: borderColor),
|
||||
borderRadius: borderRadius,
|
||||
color: color,
|
||||
),
|
||||
child: SizedBox.expand(
|
||||
child: TextButton(
|
||||
child: Text(title ?? '', style: TextStyle(color: textColor)),
|
||||
onPressed: press,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -10,26 +10,33 @@ class RoundedInputField extends StatelessWidget {
|
||||
const RoundedInputField({
|
||||
Key? key,
|
||||
this.hintText,
|
||||
this.icon = Icons.person,
|
||||
this.icon,
|
||||
this.obscureText = false,
|
||||
this.onChanged,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Icon? newIcon = icon == null
|
||||
? null
|
||||
: Icon(
|
||||
icon!,
|
||||
color: const Color(0xFF6F35A5),
|
||||
);
|
||||
|
||||
return TextFieldContainer(
|
||||
child: TextFormField(
|
||||
onChanged: onChanged,
|
||||
cursorColor: const Color(0xFF6F35A5),
|
||||
obscureText: obscureText,
|
||||
decoration: InputDecoration(
|
||||
icon: Icon(
|
||||
icon,
|
||||
color: const Color(0xFF6F35A5),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderColor: Colors.blueGrey,
|
||||
child: TextFormField(
|
||||
onChanged: onChanged,
|
||||
cursorColor: const Color(0xFF6F35A5),
|
||||
obscureText: obscureText,
|
||||
decoration: InputDecoration(
|
||||
icon: newIcon,
|
||||
hintText: hintText,
|
||||
border: InputBorder.none,
|
||||
),
|
||||
hintText: hintText,
|
||||
border: InputBorder.none,
|
||||
),
|
||||
));
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -3,21 +3,30 @@ import 'package:flutter/material.dart';
|
||||
|
||||
class TextFieldContainer extends StatelessWidget {
|
||||
final Widget child;
|
||||
final BorderRadius borderRadius;
|
||||
final Color borderColor;
|
||||
final Size? size;
|
||||
const TextFieldContainer({
|
||||
Key? key,
|
||||
required this.child,
|
||||
this.borderRadius = BorderRadius.zero,
|
||||
this.borderColor = Colors.white,
|
||||
this.size,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
double height = size == null ? 50 : size!.height;
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 10),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
height: height,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: borderColor),
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
borderRadius: borderRadius,
|
||||
),
|
||||
child: child,
|
||||
child: Align(alignment: Alignment.center, child: child),
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user