From 4f87762ed3e8c775eb9e4a0db3807637c2b40df9 Mon Sep 17 00:00:00 2001 From: Andrew Julow Date: Mon, 4 May 2020 14:04:28 -0400 Subject: [PATCH 1/9] End Part 14 --- lib/src/blocs/auth_bloc.dart | 44 ++++++++++++++++++++++++++++++++++++ pubspec.lock | 21 +++++++++++++++++ pubspec.yaml | 2 ++ 3 files changed, 67 insertions(+) create mode 100644 lib/src/blocs/auth_bloc.dart diff --git a/lib/src/blocs/auth_bloc.dart b/lib/src/blocs/auth_bloc.dart new file mode 100644 index 0000000..48d63cb --- /dev/null +++ b/lib/src/blocs/auth_bloc.dart @@ -0,0 +1,44 @@ + +import 'dart:async'; + +import 'package:rxdart/rxdart.dart'; +import 'package:rxdart/subjects.dart'; + +final RegExp regExpEmail = RegExp( + r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'); + +class AuthBloc { + final _email = BehaviorSubject(); + final _password = BehaviorSubject(); + + //Get Data + Stream get email => _email.stream.transform(validateEmail); + Stream get password => _password.stream; + Stream get isValid => CombineLatestStream.combine2(email, password, (email,password)=> true); + + //Set Data + Function(String) get changeEmail => _email.sink.add; + Function(String) get changePassword => _password.sink.add; + + dispose(){ + _email.close(); + _password.close(); + } + + //Transformers + final validateEmail = StreamTransformer.fromHandlers(handleData: (email, sink){ + if (regExpEmail.hasMatch(email.trim())){ + sink.add(email.trim()); + }else { + sink.addError('Must Be Valid Email Address'); + } + }); + + final validatePassword = StreamTransformer.fromHandlers(handleData: (password, sink){ + if (password.length >= 8){ + sink.add(password.trim()); + }else { + sink.addError('8 Character Minimum'); + } + }); +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 5747234..03cd33a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -205,6 +205,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.8" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4" path: dependency: transitive description: @@ -261,6 +268,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.2" + provider: + dependency: "direct main" + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.5+1" quiver: dependency: transitive description: @@ -268,6 +282,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.5" + rxdart: + dependency: "direct main" + description: + name: rxdart + url: "https://pub.dartlang.org" + source: hosted + version: "0.24.0" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 50c8fc6..d7f6636 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -28,6 +28,8 @@ dependencies: cloud_firestore: ^0.13.5 google_fonts: ^1.0.0 font_awesome_flutter: ^8.8.1 + rxdart: ^0.24.0 + provider: ^4.0.5+1 dev_dependencies: flutter_test: From bdb6ac21174bf65be4a345211d9c0db48f07041a Mon Sep 17 00:00:00 2001 From: Andrew Julow Date: Mon, 4 May 2020 15:28:49 -0400 Subject: [PATCH 2/9] End of Part 15 --- lib/src/app.dart | 23 ++++++++++++++-- lib/src/blocs/auth_bloc.dart | 2 +- lib/src/screens/login.dart | 49 +++++++++++++++++++++++----------- lib/src/styles/textfields.dart | 12 ++++++++- lib/src/widgets/textfield.dart | 9 +++++-- 5 files changed, 74 insertions(+), 21 deletions(-) diff --git a/lib/src/app.dart b/lib/src/app.dart index 6c93c3d..4509cd1 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -1,13 +1,32 @@ +import 'package:farmers_market/src/blocs/auth_bloc.dart'; import 'package:farmers_market/src/routes.dart'; import 'package:farmers_market/src/screens/login.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'dart:io'; -class App extends StatelessWidget { +import 'package:provider/provider.dart'; +final authBloc = AuthBloc(); + +class App extends StatefulWidget { + @override + _AppState createState() => _AppState(); +} + +class _AppState extends State { @override Widget build(BuildContext context) { - return PlatformApp(); + return MultiProvider( + providers: [ + Provider(create: (context) => authBloc) + ], + child: PlatformApp()); + } + + @override + void dispose() { + authBloc.dispose(); + super.dispose(); } } diff --git a/lib/src/blocs/auth_bloc.dart b/lib/src/blocs/auth_bloc.dart index 48d63cb..eb043be 100644 --- a/lib/src/blocs/auth_bloc.dart +++ b/lib/src/blocs/auth_bloc.dart @@ -13,7 +13,7 @@ class AuthBloc { //Get Data Stream get email => _email.stream.transform(validateEmail); - Stream get password => _password.stream; + Stream get password => _password.stream.transform(validatePassword); Stream get isValid => CombineLatestStream.combine2(email, password, (email,password)=> true); //Set Data diff --git a/lib/src/screens/login.dart b/lib/src/screens/login.dart index 2be29d3..ed79723 100644 --- a/lib/src/screens/login.dart +++ b/lib/src/screens/login.dart @@ -1,3 +1,4 @@ +import 'package:farmers_market/src/blocs/auth_bloc.dart'; import 'package:farmers_market/src/styles/base.dart'; import 'package:farmers_market/src/styles/text.dart'; import 'package:farmers_market/src/widgets/button.dart'; @@ -8,21 +9,25 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'dart:io'; +import 'package:provider/provider.dart'; + class Login extends StatelessWidget { @override Widget build(BuildContext context) { + final authBloc = Provider.of(context); + if (Platform.isIOS) { return CupertinoPageScaffold( - child: pageBody(context), + child: pageBody(context, authBloc), ); } else { return Scaffold( - body: pageBody(context), + body: pageBody(context, authBloc), ); } } - Widget pageBody(BuildContext context) { + Widget pageBody(BuildContext context, AuthBloc authBloc) { return ListView( padding: EdgeInsets.all(0.0), children: [ @@ -39,19 +44,33 @@ class Login extends StatelessWidget { image: DecorationImage(image: AssetImage('assets/images/logo.png')), ), ), - AppTextField( - isIOS: Platform.isIOS, - hintText: 'Email', - cupertinoIcon: CupertinoIcons.mail_solid, - materialIcon: Icons.email, - textInputType: TextInputType.emailAddress, + StreamBuilder( + stream: authBloc.email, + builder: (context, snapshot) { + return AppTextField( + isIOS: Platform.isIOS, + hintText: 'Email', + cupertinoIcon: CupertinoIcons.mail_solid, + materialIcon: Icons.email, + textInputType: TextInputType.emailAddress, + errorText: snapshot.error, + onChanged: authBloc.changeEmail, + ); + } ), - AppTextField( - isIOS: Platform.isIOS, - hintText: 'Password', - cupertinoIcon: IconData(0xf4c9,fontFamily: CupertinoIcons.iconFont, fontPackage: CupertinoIcons.iconFontPackage), - materialIcon: Icons.lock, - obscureText: true, + StreamBuilder( + stream: authBloc.password, + builder: (context, snapshot) { + return AppTextField( + isIOS: Platform.isIOS, + hintText: 'Password', + cupertinoIcon: IconData(0xf4c9,fontFamily: CupertinoIcons.iconFont, fontPackage: CupertinoIcons.iconFontPackage), + materialIcon: Icons.lock, + obscureText: true, + errorText: snapshot.error, + onChanged: authBloc.changePassword, + ); + } ), AppButton(buttonText: 'Login',buttonType: ButtonType.LightBlue,), SizedBox(height: 6.0,), diff --git a/lib/src/styles/textfields.dart b/lib/src/styles/textfields.dart index 9426d8e..03a3a33 100644 --- a/lib/src/styles/textfields.dart +++ b/lib/src/styles/textfields.dart @@ -32,12 +32,14 @@ abstract class TextFieldStyles { borderRadius: BorderRadius.circular(BaseStyles.borderRadius)); } - static InputDecoration materialDecoration(String hintText, IconData icon) { + static InputDecoration materialDecoration( + String hintText, IconData icon, String errorText) { return InputDecoration( contentPadding: EdgeInsets.all(8.0), hintText: hintText, hintStyle: TextFieldStyles.placeholder, border: InputBorder.none, + errorText: errorText, focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: AppColors.straw, width: BaseStyles.borderWidth), @@ -46,6 +48,14 @@ abstract class TextFieldStyles { borderSide: BorderSide(color: AppColors.straw, width: BaseStyles.borderWidth), borderRadius: BorderRadius.circular(BaseStyles.borderRadius)), + focusedErrorBorder: OutlineInputBorder( + borderSide: + BorderSide(color: AppColors.straw, width: BaseStyles.borderWidth), + borderRadius: BorderRadius.circular(BaseStyles.borderRadius)), + errorBorder: OutlineInputBorder( + borderSide: + BorderSide(color: AppColors.red, width: BaseStyles.borderWidth), + borderRadius: BorderRadius.circular(BaseStyles.borderRadius)), prefixIcon: iconPrefix(icon), ); } diff --git a/lib/src/widgets/textfield.dart b/lib/src/widgets/textfield.dart index aae8908..3205040 100644 --- a/lib/src/widgets/textfield.dart +++ b/lib/src/widgets/textfield.dart @@ -9,6 +9,8 @@ class AppTextField extends StatelessWidget{ final IconData cupertinoIcon; final TextInputType textInputType; final bool obscureText; + final void Function(String) onChanged; + final String errorText; AppTextField({ @required this.isIOS, @@ -16,7 +18,9 @@ class AppTextField extends StatelessWidget{ @required this.cupertinoIcon, @required this.materialIcon, this.textInputType = TextInputType.text, - this.obscureText = false + this.obscureText = false, + this.onChanged, + this.errorText, }); @override @@ -45,8 +49,9 @@ class AppTextField extends StatelessWidget{ cursorColor: TextFieldStyles.cursorColor, style:TextFieldStyles.text, textAlign: TextFieldStyles.textAlign, - decoration: TextFieldStyles.materialDecoration(hintText, materialIcon), + decoration: TextFieldStyles.materialDecoration(hintText, materialIcon,errorText), obscureText: obscureText, + onChanged: onChanged, ), ); } From fec423e9ffb868a34c55fd03c8118b0b79f56938 Mon Sep 17 00:00:00 2001 From: Andrew Julow Date: Wed, 6 May 2020 15:09:00 -0400 Subject: [PATCH 3/9] End of Part 16 --- lib/src/styles/text.dart | 5 ++ lib/src/styles/textfields.dart | 10 ++++ lib/src/widgets/textfield.dart | 84 +++++++++++++++++++++++++++------- 3 files changed, 82 insertions(+), 17 deletions(-) diff --git a/lib/src/styles/text.dart b/lib/src/styles/text.dart index 75bf3e8..e43273d 100644 --- a/lib/src/styles/text.dart +++ b/lib/src/styles/text.dart @@ -21,6 +21,11 @@ abstract class TextStyles { textStyle: TextStyle(color: AppColors.lightgray, fontSize: 14.0)); } + static TextStyle get error { + return GoogleFonts.roboto( + textStyle: TextStyle(color: AppColors.red, fontSize: 12.0)); + } + static TextStyle get buttonTextLight { return GoogleFonts.roboto( textStyle: TextStyle( diff --git a/lib/src/styles/textfields.dart b/lib/src/styles/textfields.dart index 03a3a33..a7999f8 100644 --- a/lib/src/styles/textfields.dart +++ b/lib/src/styles/textfields.dart @@ -32,6 +32,15 @@ abstract class TextFieldStyles { borderRadius: BorderRadius.circular(BaseStyles.borderRadius)); } + static BoxDecoration get cupertinoErrorDecoration { + return BoxDecoration( + border: Border.all( + color: AppColors.red, + width: BaseStyles.borderWidth, + ), + borderRadius: BorderRadius.circular(BaseStyles.borderRadius)); + } + static InputDecoration materialDecoration( String hintText, IconData icon, String errorText) { return InputDecoration( @@ -40,6 +49,7 @@ abstract class TextFieldStyles { hintStyle: TextFieldStyles.placeholder, border: InputBorder.none, errorText: errorText, + errorStyle: TextStyles.error, focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: AppColors.straw, width: BaseStyles.borderWidth), diff --git a/lib/src/widgets/textfield.dart b/lib/src/widgets/textfield.dart index 3205040..3032132 100644 --- a/lib/src/widgets/textfield.dart +++ b/lib/src/widgets/textfield.dart @@ -1,8 +1,9 @@ +import 'package:farmers_market/src/styles/text.dart'; import 'package:farmers_market/src/styles/textfields.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -class AppTextField extends StatelessWidget{ +class AppTextField extends StatefulWidget{ final bool isIOS; final String hintText; final IconData materialIcon; @@ -23,35 +24,84 @@ class AppTextField extends StatelessWidget{ this.errorText, }); + @override + _AppTextFieldState createState() => _AppTextFieldState(); +} + +class _AppTextFieldState extends State { + FocusNode _node; + bool displayCupertinoErrorBorder; + TextEditingController _controller; + + + @override + void initState() { + _node = FocusNode(); + _controller = TextEditingController(); + _node.addListener(_handleFocusChange); + displayCupertinoErrorBorder = false; + super.initState(); + } + + void _handleFocusChange(){ + if (_node.hasFocus==false && widget.errorText != null){ + displayCupertinoErrorBorder = true; + } else { + displayCupertinoErrorBorder = false; + } + + widget.onChanged(_controller.text); + + } + + @override + void dispose() { + _node.removeListener(_handleFocusChange); + _node.dispose(); + _controller.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { - if (isIOS){ + if (widget.isIOS){ return Padding( padding: EdgeInsets.symmetric(horizontal: TextFieldStyles.textBoxHorizontal, vertical: TextFieldStyles.textBoxVertical), - child: CupertinoTextField( - keyboardType: textInputType, - padding: EdgeInsets.all(12.0), - placeholder: hintText, - placeholderStyle: TextFieldStyles.placeholder, - style: TextFieldStyles.text, - textAlign: TextFieldStyles.textAlign, - cursorColor: TextFieldStyles.cursorColor, - decoration: TextFieldStyles.cupertinoDecoration, - prefix: TextFieldStyles.iconPrefix(cupertinoIcon), - obscureText: obscureText, + child: Column( + children: [ + CupertinoTextField( + keyboardType: widget.textInputType, + padding: EdgeInsets.all(12.0), + placeholder: widget.hintText, + placeholderStyle: TextFieldStyles.placeholder, + style: TextFieldStyles.text, + textAlign: TextFieldStyles.textAlign, + cursorColor: TextFieldStyles.cursorColor, + decoration: (displayCupertinoErrorBorder) ? TextFieldStyles.cupertinoErrorDecoration : TextFieldStyles.cupertinoDecoration, + prefix: TextFieldStyles.iconPrefix(widget.cupertinoIcon), + obscureText: widget.obscureText, + onChanged: widget.onChanged, + focusNode: _node, + controller: _controller, + ), + (widget.errorText !=null) ? Padding( + padding: const EdgeInsets.only(top: 5.0,left:10.0), + child: Row(children: [Text(widget.errorText,style: TextStyles.error,)],), + ) : Container() + ], ), ); } else { return Padding( padding: EdgeInsets.symmetric(horizontal: TextFieldStyles.textBoxHorizontal, vertical: TextFieldStyles.textBoxVertical), child: TextField( - keyboardType: textInputType, + keyboardType: widget.textInputType, cursorColor: TextFieldStyles.cursorColor, style:TextFieldStyles.text, textAlign: TextFieldStyles.textAlign, - decoration: TextFieldStyles.materialDecoration(hintText, materialIcon,errorText), - obscureText: obscureText, - onChanged: onChanged, + decoration: TextFieldStyles.materialDecoration(widget.hintText, widget.materialIcon,widget.errorText), + obscureText: widget.obscureText, + onChanged: widget.onChanged, ), ); } From d033ec029238d83d17b21fc03bddbd8ba24023ed Mon Sep 17 00:00:00 2001 From: Andrew Julow Date: Wed, 6 May 2020 16:41:16 -0400 Subject: [PATCH 4/9] End Part 17 --- lib/src/screens/login.dart | 7 ++++- lib/src/screens/signup.dart | 58 +++++++++++++++++++++++++---------- lib/src/styles/base.dart | 14 +++++++++ lib/src/widgets/button.dart | 60 +++++++++++++++++++++++++++---------- 4 files changed, 107 insertions(+), 32 deletions(-) diff --git a/lib/src/screens/login.dart b/lib/src/screens/login.dart index ed79723..dc89eaa 100644 --- a/lib/src/screens/login.dart +++ b/lib/src/screens/login.dart @@ -72,7 +72,12 @@ class Login extends StatelessWidget { ); } ), - AppButton(buttonText: 'Login',buttonType: ButtonType.LightBlue,), + StreamBuilder( + stream: authBloc.isValid, + builder: (context, snapshot) { + return AppButton(buttonText: 'Login',buttonType: (snapshot.data == true) ? ButtonType.LightBlue : ButtonType.Disabled,); + } + ), SizedBox(height: 6.0,), Center(child: Text('Or',style: TextStyles.suggestion),), SizedBox(height: 6.0,), diff --git a/lib/src/screens/signup.dart b/lib/src/screens/signup.dart index e543515..353f992 100644 --- a/lib/src/screens/signup.dart +++ b/lib/src/screens/signup.dart @@ -1,3 +1,4 @@ +import 'package:farmers_market/src/blocs/auth_bloc.dart'; import 'package:farmers_market/src/styles/base.dart'; import 'package:farmers_market/src/styles/text.dart'; import 'package:farmers_market/src/widgets/button.dart'; @@ -8,21 +9,24 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'dart:io'; +import 'package:provider/provider.dart'; + class Signup extends StatelessWidget{ @override Widget build(BuildContext context) { + final authBloc = Provider.of(context); if (Platform.isIOS){ return CupertinoPageScaffold( - child: pageBody(context), + child: pageBody(context,authBloc), ); } else { return Scaffold( - body: pageBody(context), + body: pageBody(context,authBloc), ); } } - Widget pageBody(BuildContext context) { + Widget pageBody(BuildContext context,AuthBloc authBloc) { return ListView( padding: EdgeInsets.all(0.0), children: [ @@ -39,28 +43,50 @@ class Signup extends StatelessWidget{ image: DecorationImage(image: AssetImage('assets/images/logo.png')), ), ), - AppTextField( - isIOS: Platform.isIOS, - hintText: 'Email', - cupertinoIcon: CupertinoIcons.mail_solid, - materialIcon: Icons.email, - textInputType: TextInputType.emailAddress, + StreamBuilder( + stream: authBloc.email, + builder: (context, snapshot) { + return AppTextField( + isIOS: Platform.isIOS, + hintText: 'Email', + cupertinoIcon: CupertinoIcons.mail_solid, + materialIcon: Icons.email, + textInputType: TextInputType.emailAddress, + errorText: snapshot.error, + onChanged: authBloc.changeEmail, + ); + } + ), + StreamBuilder( + stream: authBloc.password, + builder: (context, snapshot) { + return AppTextField( + isIOS: Platform.isIOS, + hintText: 'Password', + cupertinoIcon: IconData(0xf4c9,fontFamily: CupertinoIcons.iconFont, fontPackage: CupertinoIcons.iconFontPackage), + materialIcon: Icons.lock, + obscureText: true, + errorText: snapshot.error, + onChanged: authBloc.changePassword, + ); + } ), - AppTextField( - isIOS: Platform.isIOS, - hintText: 'Password', - cupertinoIcon: IconData(0xf4c9,fontFamily: CupertinoIcons.iconFont, fontPackage: CupertinoIcons.iconFontPackage), - materialIcon: Icons.lock, - obscureText: true, + StreamBuilder( + stream: authBloc.isValid, + builder: (context, snapshot) { + return AppButton(buttonText: 'Signup',buttonType: (snapshot.data == true) ? ButtonType.LightBlue : ButtonType.Disabled,); + } ), - AppButton(buttonText: 'Signup',buttonType: ButtonType.LightBlue,), + SizedBox(height: 6.0,), Center(child: Text('Or',style: TextStyles.suggestion),), + SizedBox(height: 6.0,), Padding( padding: BaseStyles.listPadding, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ AppSocialButton(socialType: SocialType.Facebook,), + SizedBox(width:15.0), AppSocialButton(socialType: SocialType.Google), ],), ), diff --git a/lib/src/styles/base.dart b/lib/src/styles/base.dart index 9596b82..1ae3048 100644 --- a/lib/src/styles/base.dart +++ b/lib/src/styles/base.dart @@ -11,6 +11,10 @@ abstract class BaseStyles{ static double get listFieldVertical => 8.0; + static double get animationOffset => 2.0; + + + static EdgeInsets get listPadding { return EdgeInsets.symmetric(horizontal: listFieldHorizontal, vertical: listFieldVertical); } @@ -25,4 +29,14 @@ abstract class BaseStyles{ ]; } + static List get boxShadowPressed { + return [ + BoxShadow( + color: AppColors.darkgray.withOpacity(.5), + offset: Offset(1.0, 1.0), + blurRadius: 1.0, + ) + ]; + } + } \ No newline at end of file diff --git a/lib/src/widgets/button.dart b/lib/src/widgets/button.dart index d0bdc04..6692bcf 100644 --- a/lib/src/widgets/button.dart +++ b/lib/src/widgets/button.dart @@ -4,7 +4,7 @@ import 'package:farmers_market/src/styles/colors.dart'; import 'package:farmers_market/src/styles/text.dart'; import 'package:flutter/material.dart'; -class AppButton extends StatelessWidget{ +class AppButton extends StatefulWidget{ final String buttonText; final ButtonType buttonType; @@ -13,12 +13,19 @@ class AppButton extends StatelessWidget{ this.buttonType }); + @override + _AppButtonState createState() => _AppButtonState(); +} + +class _AppButtonState extends State { + bool pressed = false; + @override Widget build(BuildContext context) { TextStyle fontStyle; Color buttonColor; - switch (buttonType) { + switch (widget.buttonType) { case ButtonType.Straw: fontStyle = TextStyles.buttonTextLight; buttonColor = AppColors.straw; @@ -31,8 +38,8 @@ class AppButton extends StatelessWidget{ fontStyle = TextStyles.buttonTextLight; buttonColor = AppColors.darkblue; break; - case ButtonType.LightGray: - fontStyle = TextStyles.buttonTextDark; + case ButtonType.Disabled: + fontStyle = TextStyles.buttonTextLight; buttonColor = AppColors.lightgray; break; case ButtonType.DarkGray: @@ -45,20 +52,43 @@ class AppButton extends StatelessWidget{ break; } - return Padding( - padding: BaseStyles.listPadding, - child: Container( - height: ButtonStyles.buttonHeight, - width: MediaQuery.of(context).size.width, - decoration: BoxDecoration( - color: buttonColor, - borderRadius: BorderRadius.circular(BaseStyles.borderRadius), - boxShadow: BaseStyles.boxShadow + return AnimatedContainer( + padding: EdgeInsets.only( + top: (pressed) ? BaseStyles.listFieldVertical + BaseStyles.animationOffset : BaseStyles.listFieldVertical, + bottom: (pressed) ? BaseStyles.listFieldVertical - BaseStyles.animationOffset : BaseStyles.listFieldVertical, + left: BaseStyles.listFieldHorizontal, + right: BaseStyles.listFieldHorizontal + ), + child: GestureDetector( + child: Container( + height: ButtonStyles.buttonHeight, + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + color: buttonColor, + borderRadius: BorderRadius.circular(BaseStyles.borderRadius), + boxShadow: pressed ? BaseStyles.boxShadowPressed : BaseStyles.boxShadow + ), + child: Center(child: Text(widget.buttonText,style: fontStyle,)), ), - child: Center(child: Text(buttonText,style: fontStyle,)), + onTapDown: (details){ + setState(() { + if (widget.buttonType != ButtonType.Disabled) pressed = !pressed; + }); + }, + onTapUp: (details){ + setState(() { + if (widget.buttonType != ButtonType.Disabled) pressed = !pressed; + }); + }, + onTap: (){ + if (widget.buttonType != ButtonType.Disabled) { + + } + }, ), + duration: Duration(milliseconds: 20), ); } } -enum ButtonType { LightBlue,Straw, LightGray, DarkGray, DarkBlue } \ No newline at end of file +enum ButtonType { LightBlue,Straw, Disabled, DarkGray, DarkBlue } \ No newline at end of file From 654e733d229e2226366c6f51e0eb57570b70e9e2 Mon Sep 17 00:00:00 2001 From: Andrew Julow Date: Sat, 9 May 2020 09:44:08 -0400 Subject: [PATCH 5/9] Upgrade to Flutter 1.17 --- ios/Runner.xcodeproj/project.pbxproj | 14 +------------- pubspec.lock | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 518469b..f83cfb9 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -10,12 +10,8 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3915B9972454B544000BD821 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3915B9962454B544000BD821 /* GoogleService-Info.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 68A0E9B6A28F5130E373EEE2 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FE17B991E2D6714A5E1D4FF5 /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -28,8 +24,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -41,7 +35,6 @@ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3915B9962454B544000BD821 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 4B20A56B8C0449CF4994B84D /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 65A2F69D4D2E6DBD65A20F91 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 6CE5340600B65B28702F21B5 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; @@ -50,7 +43,6 @@ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -64,8 +56,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 68A0E9B6A28F5130E373EEE2 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -84,9 +74,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -233,7 +221,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; 79635FBF6A90A2364C399DB8 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; diff --git a/pubspec.lock b/pubspec.lock index 03cd33a..2b32d60 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,35 +7,35 @@ packages: name: archive url: "https://pub.dartlang.org" source: hosted - version: "2.0.11" + version: "2.0.13" args: dependency: transitive description: name: args url: "https://pub.dartlang.org" source: hosted - version: "1.5.2" + version: "1.6.0" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "2.4.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "2.0.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.1.2" + version: "1.1.3" cloud_firestore: dependency: "direct main" description: @@ -63,7 +63,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.11" + version: "1.14.12" convert: dependency: transitive description: @@ -77,7 +77,7 @@ packages: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.4" cupertino_icons: dependency: "direct main" description: @@ -183,7 +183,7 @@ packages: name: image url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.12" js: dependency: transitive description: @@ -281,7 +281,7 @@ packages: name: quiver url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.1.3" rxdart: dependency: "direct main" description: @@ -300,7 +300,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.5.5" + version: "1.7.0" stack_trace: dependency: transitive description: @@ -335,7 +335,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.11" + version: "0.2.15" typed_data: dependency: transitive description: @@ -356,7 +356,7 @@ packages: name: xml url: "https://pub.dartlang.org" source: hosted - version: "3.5.0" + version: "3.6.1" sdks: dart: ">=2.7.0 <3.0.0" flutter: ">=1.12.13+hotfix.5 <2.0.0" From 340d5d6b2bcc99f6fa4120ac5b72f46d06745644 Mon Sep 17 00:00:00 2001 From: Andrew Julow Date: Sat, 9 May 2020 10:43:26 -0400 Subject: [PATCH 6/9] End of Part 18 --- lib/src/blocs/auth_bloc.dart | 19 +++++++++++++++++++ lib/src/models/user.dart | 17 +++++++++++++++++ lib/src/screens/signup.dart | 2 +- lib/src/services/firestore_service.dart | 14 ++++++++++++++ lib/src/widgets/button.dart | 6 ++++-- 5 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 lib/src/models/user.dart create mode 100644 lib/src/services/firestore_service.dart diff --git a/lib/src/blocs/auth_bloc.dart b/lib/src/blocs/auth_bloc.dart index eb043be..336be41 100644 --- a/lib/src/blocs/auth_bloc.dart +++ b/lib/src/blocs/auth_bloc.dart @@ -1,6 +1,9 @@ import 'dart:async'; +import 'package:farmers_market/src/models/user.dart'; +import 'package:farmers_market/src/services/firestore_service.dart'; +import 'package:firebase_auth/firebase_auth.dart'; import 'package:rxdart/rxdart.dart'; import 'package:rxdart/subjects.dart'; @@ -10,6 +13,8 @@ final RegExp regExpEmail = RegExp( class AuthBloc { final _email = BehaviorSubject(); final _password = BehaviorSubject(); + final FirebaseAuth _auth = FirebaseAuth.instance; + final FirestoreService _firestoreService = FirestoreService(); //Get Data Stream get email => _email.stream.transform(validateEmail); @@ -41,4 +46,18 @@ class AuthBloc { sink.addError('8 Character Minimum'); } }); + + //Functions + signupEmail() async{ + print('Signing up with username and password'); + + try{ + AuthResult authResult = await _auth.createUserWithEmailAndPassword(email: _email.value.trim(), password: _password.value.trim()); + var user = User(userId: authResult.user.uid, email: _email.value.trim()); + await _firestoreService.addUser(user); + } catch (error){ + print(error); + } + } + } \ No newline at end of file diff --git a/lib/src/models/user.dart b/lib/src/models/user.dart new file mode 100644 index 0000000..18e4968 --- /dev/null +++ b/lib/src/models/user.dart @@ -0,0 +1,17 @@ +class User { + final String userId; + final String email; + + User({this.email, this.userId}); + + Map toMap(){ + return { + 'userId': userId, + 'email': email + }; + } + + User.fromFirestore(Map firestore) + : userId = firestore['userId'], + email = firestore['email']; +} \ No newline at end of file diff --git a/lib/src/screens/signup.dart b/lib/src/screens/signup.dart index 353f992..529296a 100644 --- a/lib/src/screens/signup.dart +++ b/lib/src/screens/signup.dart @@ -74,7 +74,7 @@ class Signup extends StatelessWidget{ StreamBuilder( stream: authBloc.isValid, builder: (context, snapshot) { - return AppButton(buttonText: 'Signup',buttonType: (snapshot.data == true) ? ButtonType.LightBlue : ButtonType.Disabled,); + return AppButton(buttonText: 'Signup',buttonType: (snapshot.data == true) ? ButtonType.LightBlue : ButtonType.Disabled, onPressed: authBloc.signupEmail,); } ), SizedBox(height: 6.0,), diff --git a/lib/src/services/firestore_service.dart b/lib/src/services/firestore_service.dart new file mode 100644 index 0000000..71a92a5 --- /dev/null +++ b/lib/src/services/firestore_service.dart @@ -0,0 +1,14 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:farmers_market/src/models/user.dart'; + +class FirestoreService { + Firestore _db = Firestore.instance; + + Future addUser(User user){ + return _db.collection('users').document(user.userId).setData(user.toMap()); + } + + Future fetchUser(String userId){ + return _db.collection('users').document(userId).get().then((doc) => User.fromFirestore(doc.data)); + } +} \ No newline at end of file diff --git a/lib/src/widgets/button.dart b/lib/src/widgets/button.dart index 6692bcf..5e218ad 100644 --- a/lib/src/widgets/button.dart +++ b/lib/src/widgets/button.dart @@ -7,10 +7,12 @@ import 'package:flutter/material.dart'; class AppButton extends StatefulWidget{ final String buttonText; final ButtonType buttonType; + final void Function() onPressed; AppButton({ @required this.buttonText, - this.buttonType + this.buttonType, + this.onPressed }); @override @@ -82,7 +84,7 @@ class _AppButtonState extends State { }, onTap: (){ if (widget.buttonType != ButtonType.Disabled) { - + widget.onPressed(); } }, ), From b944d6216052f9ae20400518885bc43d3d4cbed0 Mon Sep 17 00:00:00 2001 From: Andrew Julow Date: Sat, 9 May 2020 14:35:13 -0400 Subject: [PATCH 7/9] change doc to snapshot --- lib/src/services/firestore_service.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/services/firestore_service.dart b/lib/src/services/firestore_service.dart index 71a92a5..50e5ebf 100644 --- a/lib/src/services/firestore_service.dart +++ b/lib/src/services/firestore_service.dart @@ -9,6 +9,6 @@ class FirestoreService { } Future fetchUser(String userId){ - return _db.collection('users').document(userId).get().then((doc) => User.fromFirestore(doc.data)); + return _db.collection('users').document(userId).get().then((snapshot) => User.fromFirestore(snapshot.data)); } } \ No newline at end of file From 58ab25d3fea609a8ba5bdd3463a7c678e3f05994 Mon Sep 17 00:00:00 2001 From: Andrew Julow Date: Mon, 11 May 2020 14:22:26 -0400 Subject: [PATCH 8/9] End Part 20 --- lib/src/app.dart | 21 ++++-- lib/src/blocs/auth_bloc.dart | 27 ++++++- lib/src/routes.dart | 4 +- lib/src/screens/landing.dart | 2 +- lib/src/screens/login.dart | 136 ++++++++++++++++++++--------------- lib/src/screens/signup.dart | 18 ++++- 6 files changed, 141 insertions(+), 67 deletions(-) diff --git a/lib/src/app.dart b/lib/src/app.dart index 4509cd1..83cfe51 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -1,5 +1,6 @@ import 'package:farmers_market/src/blocs/auth_bloc.dart'; import 'package:farmers_market/src/routes.dart'; +import 'package:farmers_market/src/screens/landing.dart'; import 'package:farmers_market/src/screens/login.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -18,7 +19,8 @@ class _AppState extends State { Widget build(BuildContext context) { return MultiProvider( providers: [ - Provider(create: (context) => authBloc) + Provider(create: (context) => authBloc), + FutureProvider(create: (context) => authBloc.isLoggedIn()) ], child: PlatformApp()); } @@ -31,22 +33,33 @@ class _AppState extends State { } class PlatformApp extends StatelessWidget { + @override Widget build(BuildContext context) { + + var isLoggedIn = Provider.of(context); + if (Platform.isIOS) { return CupertinoApp( - home: Login(), + home: (isLoggedIn == null) ? loadingScreen(true) : (isLoggedIn == true ) ? Landing() : Login(), onGenerateRoute: Routes.cupertinoRoutes, theme: CupertinoThemeData( scaffoldBackgroundColor: Colors.white ) - ); + ); } else { return MaterialApp( - home: Login(), + home: (isLoggedIn == null) ? loadingScreen(false) : (isLoggedIn == true ) ? Landing() : Login(), onGenerateRoute: Routes.materialRoutes, theme: ThemeData(scaffoldBackgroundColor: Colors.white) ); } } + + Widget loadingScreen(bool isIOS){ + return (isIOS) + ? CupertinoPageScaffold(child: Center(child: CupertinoActivityIndicator(),),) + : Scaffold(body: Center(child: CircularProgressIndicator())); + } + } diff --git a/lib/src/blocs/auth_bloc.dart b/lib/src/blocs/auth_bloc.dart index 336be41..8fab0e0 100644 --- a/lib/src/blocs/auth_bloc.dart +++ b/lib/src/blocs/auth_bloc.dart @@ -13,6 +13,7 @@ final RegExp regExpEmail = RegExp( class AuthBloc { final _email = BehaviorSubject(); final _password = BehaviorSubject(); + final _user = BehaviorSubject(); final FirebaseAuth _auth = FirebaseAuth.instance; final FirestoreService _firestoreService = FirestoreService(); @@ -20,6 +21,7 @@ class AuthBloc { Stream get email => _email.stream.transform(validateEmail); Stream get password => _password.stream.transform(validatePassword); Stream get isValid => CombineLatestStream.combine2(email, password, (email,password)=> true); + Stream get user => _user.stream; //Set Data Function(String) get changeEmail => _email.sink.add; @@ -28,6 +30,7 @@ class AuthBloc { dispose(){ _email.close(); _password.close(); + _user.close(); } //Transformers @@ -49,15 +52,35 @@ class AuthBloc { //Functions signupEmail() async{ - print('Signing up with username and password'); - try{ AuthResult authResult = await _auth.createUserWithEmailAndPassword(email: _email.value.trim(), password: _password.value.trim()); var user = User(userId: authResult.user.uid, email: _email.value.trim()); await _firestoreService.addUser(user); + _user.sink.add(user); } catch (error){ print(error); } } + loginEmail() async{ + try{ + AuthResult authResult = await _auth.signInWithEmailAndPassword(email: _email.value.trim(), password: _password.value.trim()); + var user = await _firestoreService.fetchUser(authResult.user.uid); + _user.sink.add(user); + } catch (error){ + print(error); + } + } + + Future isLoggedIn() async { + var firebaseUser = await _auth.currentUser(); + if (firebaseUser == null) return false; + + var user = await _firestoreService.fetchUser(firebaseUser.uid); + if (user == null) return false; + + _user.sink.add(user); + return true; + } + } \ No newline at end of file diff --git a/lib/src/routes.dart b/lib/src/routes.dart index e7bfee6..1e4fdfc 100644 --- a/lib/src/routes.dart +++ b/lib/src/routes.dart @@ -8,7 +8,7 @@ abstract class Routes { static MaterialPageRoute materialRoutes(RouteSettings settings){ switch(settings.name){ - case "/": + case "/landing": return MaterialPageRoute(builder: (context) => Landing()); case "/signup": return MaterialPageRoute(builder: (context) => Signup()); @@ -21,7 +21,7 @@ abstract class Routes { static CupertinoPageRoute cupertinoRoutes(RouteSettings settings){ switch(settings.name){ - case "/": + case "/landing": return CupertinoPageRoute(builder: (context) => Landing()); case "/signup": return CupertinoPageRoute(builder: (context) => Signup()); diff --git a/lib/src/screens/landing.dart b/lib/src/screens/landing.dart index 2f38e16..5d164c6 100644 --- a/lib/src/screens/landing.dart +++ b/lib/src/screens/landing.dart @@ -3,6 +3,6 @@ import 'package:flutter/material.dart'; class Landing extends StatelessWidget{ @override Widget build(BuildContext context) { - return Scaffold(); + return Scaffold(body: Center(child: Text('Landing'),)); } } \ No newline at end of file diff --git a/lib/src/screens/login.dart b/lib/src/screens/login.dart index dc89eaa..513a327 100644 --- a/lib/src/screens/login.dart +++ b/lib/src/screens/login.dart @@ -11,7 +11,22 @@ import 'dart:io'; import 'package:provider/provider.dart'; -class Login extends StatelessWidget { +class Login extends StatefulWidget { + @override + _LoginState createState() => _LoginState(); +} + +class _LoginState extends State { + + @override + void initState() { + final authBloc = Provider.of(context,listen: false); + authBloc.user.listen((user) { + if (user != null) Navigator.pushReplacementNamed(context, '/landing'); + }); + super.initState(); + } + @override Widget build(BuildContext context) { final authBloc = Provider.of(context); @@ -45,74 +60,83 @@ class Login extends StatelessWidget { ), ), StreamBuilder( - stream: authBloc.email, - builder: (context, snapshot) { - return AppTextField( - isIOS: Platform.isIOS, - hintText: 'Email', - cupertinoIcon: CupertinoIcons.mail_solid, - materialIcon: Icons.email, - textInputType: TextInputType.emailAddress, - errorText: snapshot.error, - onChanged: authBloc.changeEmail, - ); - } - ), + stream: authBloc.email, + builder: (context, snapshot) { + return AppTextField( + isIOS: Platform.isIOS, + hintText: 'Email', + cupertinoIcon: CupertinoIcons.mail_solid, + materialIcon: Icons.email, + textInputType: TextInputType.emailAddress, + errorText: snapshot.error, + onChanged: authBloc.changeEmail, + ); + }), StreamBuilder( - stream: authBloc.password, - builder: (context, snapshot) { - return AppTextField( - isIOS: Platform.isIOS, - hintText: 'Password', - cupertinoIcon: IconData(0xf4c9,fontFamily: CupertinoIcons.iconFont, fontPackage: CupertinoIcons.iconFontPackage), - materialIcon: Icons.lock, - obscureText: true, - errorText: snapshot.error, - onChanged: authBloc.changePassword, - ); - } - ), + stream: authBloc.password, + builder: (context, snapshot) { + return AppTextField( + isIOS: Platform.isIOS, + hintText: 'Password', + cupertinoIcon: IconData(0xf4c9, + fontFamily: CupertinoIcons.iconFont, + fontPackage: CupertinoIcons.iconFontPackage), + materialIcon: Icons.lock, + obscureText: true, + errorText: snapshot.error, + onChanged: authBloc.changePassword, + ); + }), StreamBuilder( - stream: authBloc.isValid, - builder: (context, snapshot) { - return AppButton(buttonText: 'Login',buttonType: (snapshot.data == true) ? ButtonType.LightBlue : ButtonType.Disabled,); - } + stream: authBloc.isValid, + builder: (context, snapshot) { + return AppButton( + buttonText: 'Login', + buttonType: (snapshot.data == true) + ? ButtonType.LightBlue + : ButtonType.Disabled, + onPressed: authBloc.loginEmail, + ); + }), + SizedBox( + height: 6.0, + ), + Center( + child: Text('Or', style: TextStyles.suggestion), + ), + SizedBox( + height: 6.0, ), - SizedBox(height: 6.0,), - Center(child: Text('Or',style: TextStyles.suggestion),), - SizedBox(height: 6.0,), Padding( padding: BaseStyles.listPadding, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - AppSocialButton(socialType: SocialType.Facebook,), - SizedBox(width:15.0), + AppSocialButton( + socialType: SocialType.Facebook, + ), + SizedBox(width: 15.0), AppSocialButton(socialType: SocialType.Google), - ],), + ], + ), ), - Padding( + Padding( padding: BaseStyles.listPadding, - child: RichText( - textAlign: TextAlign.center, - text: TextSpan( - text: 'New Here? ', - style: TextStyles.body, - children: [ - TextSpan( - text: 'Signup', - style: TextStyles.link, - recognizer: TapGestureRecognizer() - ..onTap = () => Navigator.pushNamed(context, '/signup') - ) - ] - ) - ), + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + text: 'New Here? ', + style: TextStyles.body, + children: [ + TextSpan( + text: 'Signup', + style: TextStyles.link, + recognizer: TapGestureRecognizer() + ..onTap = + () => Navigator.pushNamed(context, '/signup')) + ])), ) ], ); } - - - } diff --git a/lib/src/screens/signup.dart b/lib/src/screens/signup.dart index 529296a..12ba3f7 100644 --- a/lib/src/screens/signup.dart +++ b/lib/src/screens/signup.dart @@ -11,7 +11,22 @@ import 'dart:io'; import 'package:provider/provider.dart'; -class Signup extends StatelessWidget{ +class Signup extends StatefulWidget{ + @override + _SignupState createState() => _SignupState(); +} + +class _SignupState extends State { + + @override + void initState() { + final authBloc = Provider.of(context,listen: false); + authBloc.user.listen((user) { + if (user != null) Navigator.pushReplacementNamed(context, '/landing'); + }); + super.initState(); + } + @override Widget build(BuildContext context) { final authBloc = Provider.of(context); @@ -111,5 +126,4 @@ class Signup extends StatelessWidget{ ], ); } - } \ No newline at end of file From 82ac8693c79a05963088eaf94cdea600ee5c8574 Mon Sep 17 00:00:00 2001 From: Andrew Julow Date: Tue, 12 May 2020 13:55:13 -0400 Subject: [PATCH 9/9] End of Part 21 --- lib/src/routes.dart | 32 ++++++++++++++++++-------------- lib/src/screens/landing.dart | 24 +++++++++++++++++++++++- lib/src/screens/vendor.dart | 24 ++++++++++++++++++++++++ lib/src/widgets/navbar.dart | 10 ++++++++++ 4 files changed, 75 insertions(+), 15 deletions(-) create mode 100644 lib/src/screens/vendor.dart create mode 100644 lib/src/widgets/navbar.dart diff --git a/lib/src/routes.dart b/lib/src/routes.dart index 1e4fdfc..3351b55 100644 --- a/lib/src/routes.dart +++ b/lib/src/routes.dart @@ -1,34 +1,38 @@ import 'package:farmers_market/src/screens/landing.dart'; import 'package:farmers_market/src/screens/login.dart'; import 'package:farmers_market/src/screens/signup.dart'; +import 'package:farmers_market/src/screens/vendor.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; abstract class Routes { - - static MaterialPageRoute materialRoutes(RouteSettings settings){ - switch(settings.name){ + static MaterialPageRoute materialRoutes(RouteSettings settings) { + switch (settings.name) { case "/landing": - return MaterialPageRoute(builder: (context) => Landing()); + return MaterialPageRoute(builder: (context) => Landing()); case "/signup": - return MaterialPageRoute(builder: (context) => Signup()); + return MaterialPageRoute(builder: (context) => Signup()); case "/login": - return MaterialPageRoute(builder: (context) => Login()); + return MaterialPageRoute(builder: (context) => Login()); + case "/vendor": + return MaterialPageRoute(builder: (context) => Vendor()); default: - return MaterialPageRoute(builder: (context) => Login()); + return MaterialPageRoute(builder: (context) => Login()); } } - static CupertinoPageRoute cupertinoRoutes(RouteSettings settings){ - switch(settings.name){ + static CupertinoPageRoute cupertinoRoutes(RouteSettings settings) { + switch (settings.name) { case "/landing": - return CupertinoPageRoute(builder: (context) => Landing()); + return CupertinoPageRoute(builder: (context) => Landing()); case "/signup": - return CupertinoPageRoute(builder: (context) => Signup()); + return CupertinoPageRoute(builder: (context) => Signup()); case "/login": - return CupertinoPageRoute(builder: (context) => Login()); + return CupertinoPageRoute(builder: (context) => Login()); + case "/vendor": + return CupertinoPageRoute(builder: (context) => Vendor()); default: - return CupertinoPageRoute(builder: (context) => Login()); + return CupertinoPageRoute(builder: (context) => Login()); } } -} \ No newline at end of file +} diff --git a/lib/src/screens/landing.dart b/lib/src/screens/landing.dart index 5d164c6..d9e8b4e 100644 --- a/lib/src/screens/landing.dart +++ b/lib/src/screens/landing.dart @@ -1,8 +1,30 @@ +import 'package:farmers_market/src/widgets/button.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'dart:io'; class Landing extends StatelessWidget{ @override Widget build(BuildContext context) { - return Scaffold(body: Center(child: Text('Landing'),)); + if (Platform.isIOS){ + return CupertinoPageScaffold( + child: pageBody(context), + ); + } else { + return Scaffold(body:pageBody(context)); + } + } + + Widget pageBody(BuildContext context){ + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + AppButton( + buttonText: 'Vendor Page', + buttonType: ButtonType.Straw, + onPressed: () => Navigator.pushNamed(context, '/vendor'), + ) + ], + ); } } \ No newline at end of file diff --git a/lib/src/screens/vendor.dart b/lib/src/screens/vendor.dart new file mode 100644 index 0000000..483d8e3 --- /dev/null +++ b/lib/src/screens/vendor.dart @@ -0,0 +1,24 @@ +import 'package:farmers_market/src/widgets/navbar.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'dart:io'; + +class Vendor extends StatelessWidget { + @override + Widget build(BuildContext context) { + + if (Platform.isIOS) { + return CupertinoPageScaffold( + child: NestedScrollView( + headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled){ + return [ + AppNavbar.cupertinoNavBar(title: 'Vendor Name', context:context), + ]; + }, + body: Center(child: Text('Placeholder'),)), + ); + } else { + return Center(child: Scaffold(body: Text('Material'),)); + } + } +} \ No newline at end of file diff --git a/lib/src/widgets/navbar.dart b/lib/src/widgets/navbar.dart new file mode 100644 index 0000000..be1f807 --- /dev/null +++ b/lib/src/widgets/navbar.dart @@ -0,0 +1,10 @@ +import 'package:flutter/cupertino.dart'; + +abstract class AppNavbar { + + static CupertinoSliverNavigationBar cupertinoNavBar ({String title, BuildContext context}) { + return CupertinoSliverNavigationBar( + largeTitle: Text(title), + ); + } +} \ No newline at end of file