Last active
June 8, 2021 14:44
-
-
Save tolo/1e39cf49bbc5e0492969c027c47b0c45 to your computer and use it in GitHub Desktop.
Flutter Labinar - Lab 2 (complete)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
void main() => runApp(SignUpApp()); | |
class SignUpApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
debugShowCheckedModeBanner: false, | |
routes: { | |
'/': (context) => LandingScreen(), | |
'/signup': (context) => SignUpScreen(), | |
'/welcome': (context) => WelcomeScreen(), | |
}, | |
onUnknownRoute: (context) => MaterialPageRoute(builder: (_) => Four04()), | |
); | |
} | |
} | |
/// Screens / Pages | |
class LandingScreen extends StatelessWidget { | |
void login(BuildContext context) => Navigator.of(context).pushNamed('/welcome'); | |
void signup(BuildContext context) => Navigator.of(context).pushNamed('/signup'); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
backgroundColor: Colors.grey[200], | |
body: Center( | |
child: SafeArea( | |
child: Column( children: [ | |
Container(height: MediaQuery.of(context).size.height * 0.33, | |
margin: EdgeInsets.all(20), | |
decoration: BoxDecoration( | |
gradient: LinearGradient( | |
begin: Alignment.topRight, | |
end: Alignment.bottomLeft, | |
colors: [Colors.blue, Colors.red], | |
), | |
borderRadius: BorderRadius.circular(8), | |
),), | |
Text('Welcome', style: Theme.of(context).textTheme.headline3,), | |
SizedBox(height: 40), | |
CustomIconButton(icon: Icons.login, title: 'Login', onPressed: () => login(context)), | |
SizedBox(height: 8), | |
CustomIconButton(icon: Icons.login, title: 'Signup', onPressed: () => signup(context)), | |
]), | |
), | |
), | |
); | |
} | |
} | |
class SignUpScreen extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar(title: Text('Sign up'),), | |
backgroundColor: Colors.grey[200], | |
body: Center( | |
child: Container( | |
margin: EdgeInsets.all(16), | |
constraints: BoxConstraints(maxWidth: 400), | |
child: Card( | |
child: SignUpForm(), | |
), | |
), | |
), | |
); | |
} | |
} | |
class WelcomeScreen extends StatelessWidget { | |
void _showToast(BuildContext context) { | |
final snackBar = SnackBar(backgroundColor: Colors.purple, | |
content: Text('A little snack (or toast if you will)! 🚀')); | |
ScaffoldMessenger.of(context).showSnackBar(snackBar); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar(title: Text('Welcome'), actions: [ | |
IconButton( | |
icon: Icon(Icons.logout, color: Colors.white), | |
onPressed: () => Navigator.of(context).popToRoot(), | |
) | |
]), | |
body: Center( | |
child: Builder(builder: (c) { | |
return GestureDetector(onTap: () => _showToast(context), child: | |
Text('Welcome to the app!', style: Theme.of(context).textTheme.headline2, textAlign: TextAlign.center)); | |
}), | |
), | |
); | |
} | |
} | |
class Four04 extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar(title: Text('Welcome'),), | |
body: Center( | |
child: | |
Column(mainAxisAlignment: MainAxisAlignment.center, children: [ | |
Icon(Icons.warning, size: 96, color: Colors.orange), | |
Text('404', style: Theme.of(context).textTheme.headline1), | |
Text('Page not found...'), | |
Text('😬', style: TextStyle(fontSize: 72)), | |
]) | |
), | |
); | |
} | |
} | |
/// Components | |
class SignUpForm extends StatefulWidget { | |
@override | |
_SignUpFormState createState() => _SignUpFormState(); | |
} | |
class _SignUpFormState extends State<SignUpForm> { | |
final _firstNameTextController = TextEditingController(); | |
final _lastNameTextController = TextEditingController(); | |
final _usernameTextController = TextEditingController(); | |
double _formProgress = 0; | |
void _updateFormProgress() { | |
var progress = 0.0; | |
final controllers = [ | |
_firstNameTextController, | |
_lastNameTextController, | |
_usernameTextController | |
]; | |
for (final controller in controllers) { | |
if (controller.value.text.isNotEmpty) { | |
progress += 1 / controllers.length; | |
} | |
} | |
setState(() { | |
_formProgress = progress; | |
}); | |
} | |
void _showWelcomeScreen() { | |
Navigator.of(context).pushNamed('/welcome'); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Form( | |
onChanged: _updateFormProgress, | |
child: Column( | |
mainAxisSize: MainAxisSize.min, | |
children: [ | |
AnimatedProgressIndicator(value: _formProgress), | |
Text('Sign up', style: Theme.of(context).textTheme.headline4), | |
Padding( | |
padding: EdgeInsets.all(8.0), | |
child: TextFormField( | |
controller: _firstNameTextController, | |
decoration: InputDecoration(hintText: 'First name'), | |
), | |
), | |
Padding( | |
padding: EdgeInsets.all(8.0), | |
child: TextFormField( | |
controller: _lastNameTextController, | |
decoration: InputDecoration(hintText: 'Last name'), | |
), | |
), | |
Padding( | |
padding: EdgeInsets.all(8.0), | |
child: TextFormField( | |
controller: _usernameTextController, | |
decoration: InputDecoration(hintText: 'Username'), | |
), | |
), | |
TextButton( | |
style: ButtonStyle( | |
foregroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) { | |
return states.contains(MaterialState.disabled) ? null : Colors.white; | |
}), | |
backgroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) { | |
return states.contains(MaterialState.disabled) ? null : Colors.blue; | |
}), | |
), | |
onPressed: _formProgress == 1 ? _showWelcomeScreen : null, | |
child: Text('Sign up'), | |
), | |
], | |
), | |
); | |
} | |
} | |
class AnimatedProgressIndicator extends StatefulWidget { | |
final double value; | |
AnimatedProgressIndicator({ | |
required this.value, | |
}); | |
@override | |
State<StatefulWidget> createState() { | |
return _AnimatedProgressIndicatorState(); | |
} | |
} | |
class _AnimatedProgressIndicatorState extends State<AnimatedProgressIndicator> | |
with SingleTickerProviderStateMixin { | |
late AnimationController _controller; | |
late Animation<Color?> _colorAnimation; | |
late Animation<double> _curveAnimation; | |
void initState() { | |
super.initState(); | |
_controller = AnimationController( | |
duration: Duration(milliseconds: 1200), vsync: this); | |
final colorTween = TweenSequence([ | |
TweenSequenceItem( | |
tween: ColorTween(begin: Colors.red, end: Colors.orange), | |
weight: 1, | |
), | |
TweenSequenceItem( | |
tween: ColorTween(begin: Colors.orange, end: Colors.yellow), | |
weight: 1, | |
), | |
TweenSequenceItem( | |
tween: ColorTween(begin: Colors.yellow, end: Colors.green), | |
weight: 1, | |
), | |
]); | |
_colorAnimation = _controller.drive(colorTween); | |
_curveAnimation = _controller.drive(CurveTween(curve: Curves.easeIn)); | |
} | |
void didUpdateWidget(oldWidget) { | |
super.didUpdateWidget(oldWidget); | |
_controller.animateTo(widget.value); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return AnimatedBuilder( | |
animation: _controller, | |
builder: (context, child) => LinearProgressIndicator( | |
value: _curveAnimation.value, | |
valueColor: _colorAnimation, | |
backgroundColor: _colorAnimation.value?.withOpacity(0.4), | |
), | |
); | |
} | |
} | |
class CustomIconButton extends StatelessWidget { | |
final IconData icon; | |
final String title; | |
final VoidCallback onPressed; | |
const CustomIconButton({Key? key, required this.icon, required this.title, required this.onPressed}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
final buttonContent = Container(height: 44, child: | |
Row(children: [ | |
Icon(icon, size: 24), | |
SizedBox(width: 8), | |
Text(title) , | |
]), | |
); | |
final button = ElevatedButton(onPressed: onPressed, child: buttonContent); | |
return Row(mainAxisSize: MainAxisSize.max, children: [ | |
SizedBox(width: 32), | |
Expanded(child: button), | |
SizedBox(width: 32), | |
]); | |
} | |
} | |
extension on NavigatorState { | |
void popToRoot() { | |
popUntil((route) => route.settings.name == '/'); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment