Skip to content

Instantly share code, notes, and snippets.

@tolo
Last active June 8, 2021 14:44
Show Gist options
  • Save tolo/1e39cf49bbc5e0492969c027c47b0c45 to your computer and use it in GitHub Desktop.
Save tolo/1e39cf49bbc5e0492969c027c47b0c45 to your computer and use it in GitHub Desktop.
Flutter Labinar - Lab 2 (complete)
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