Created
February 27, 2020 20:00
-
-
Save slightfoot/4c2b504959e7fcd793aaaf8f0e0ee0a1 to your computer and use it in GitHub Desktop.
Curved Background Login Form Example - Utilises overdraw of the CustomPaint to draw the curve behind the content even when the keyboard appears.
This file contains hidden or 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/cupertino.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/services.dart'; | |
void main() => runApp(App()); | |
class App extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
theme: ThemeData( | |
brightness: Brightness.light, | |
primaryColor: Color(0xFF3A455C), | |
inputDecorationTheme: InputDecorationTheme( | |
contentPadding: EdgeInsets.all(8.0), | |
border: OutlineInputBorder( | |
borderRadius: BorderRadius.circular(48.0), | |
), | |
), | |
buttonColor: Color(0xFF3A455C), | |
buttonTheme: ButtonThemeData( | |
buttonColor: Color(0xFF3A455C), | |
textTheme: ButtonTextTheme.primary, | |
shape: StadiumBorder(), | |
height: kMinInteractiveDimension, | |
), | |
), | |
home: LoginScreen(), | |
); | |
} | |
} | |
class LoginScreen extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return LayoutBuilder( | |
builder: (BuildContext context, BoxConstraints constraints) { | |
return Scaffold( | |
body: LoginForm( | |
height: constraints.maxHeight, | |
), | |
); | |
}, | |
); | |
} | |
} | |
class LoginForm extends StatelessWidget { | |
const LoginForm({ | |
Key key, | |
@required this.height, | |
}) : super(key: key); | |
final double height; | |
@override | |
Widget build(BuildContext context) { | |
return Column( | |
mainAxisAlignment: MainAxisAlignment.end, | |
crossAxisAlignment: CrossAxisAlignment.stretch, | |
children: <Widget>[ | |
CustomPaint( | |
painter: _CurvePainter( | |
height: height, | |
foreground: Theme.of(context).primaryColor, | |
), | |
), | |
Padding( | |
padding: const EdgeInsets.symmetric(horizontal: 32.0), | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.stretch, | |
children: <Widget>[ | |
TextField( | |
decoration: InputDecoration( | |
prefixIcon: Icon(Icons.person), | |
), | |
), | |
SizedBox(height: 16.0), | |
TextField( | |
decoration: InputDecoration( | |
prefixIcon: Icon(Icons.lock), | |
), | |
), | |
SizedBox(height: 16.0), | |
RaisedButton( | |
onPressed: () {}, | |
child: Text('SIGN IN'), | |
), | |
SizedBox(height: 8.0), | |
FlatButton( | |
onPressed: () {}, | |
child: Text( | |
'Forgot Password?', | |
style: TextStyle( | |
color: Color(0xFF8fA4f7), | |
decoration: TextDecoration.underline, | |
), | |
textAlign: TextAlign.left, | |
), | |
), | |
SizedBox(height: 8.0), | |
Row( | |
children: <Widget>[ | |
Expanded( | |
child: RaisedButton( | |
onPressed: () {}, | |
shape: RoundedRectangleBorder( | |
borderRadius: BorderRadius.circular(2.0), | |
), | |
color: Color(0xFF404988), | |
child: Text( | |
'FACEBOOK', | |
style: TextStyle( | |
fontSize: 18.0, | |
letterSpacing: 1.25, | |
), | |
), | |
), | |
), | |
const SizedBox(width: 4.0), | |
Expanded( | |
child: RaisedButton( | |
onPressed: () {}, | |
shape: RoundedRectangleBorder( | |
borderRadius: BorderRadius.circular(2.0), | |
), | |
color: Color(0xFF5C6EE8), | |
child: Text( | |
'GOOGLE', | |
style: TextStyle( | |
fontSize: 18.0, | |
letterSpacing: 1.25, | |
), | |
), | |
), | |
), | |
], | |
), | |
SizedBox(height: 8.0), | |
FlatButton( | |
onPressed: () {}, | |
child: Text.rich( | |
TextSpan( | |
children: <TextSpan>[ | |
TextSpan( | |
text: 'Don’t have an account? ', | |
), | |
TextSpan( | |
text: 'Sign up', | |
style: TextStyle( | |
color: Color(0xFF8fA4f7), | |
decoration: TextDecoration.underline, | |
), | |
), | |
], | |
), | |
style: TextStyle( | |
color: Color(0xFF656565), | |
), | |
textAlign: TextAlign.left, | |
), | |
), | |
SizedBox(height: 32.0), | |
], | |
), | |
), | |
], | |
); | |
} | |
} | |
class _CurvePainter extends CustomPainter { | |
const _CurvePainter({ | |
@required this.height, | |
@required this.foreground, | |
}); | |
final double height; | |
final Color foreground; | |
@override | |
void paint(Canvas canvas, Size size) { | |
final height20 = height / 5.0; | |
final top = height20; | |
final mid = height20 * 2.0; | |
final bottom = height20 * 2.5; | |
final center = size.width / 2.0; | |
canvas.save(); | |
canvas.translate(0.0, -(height * 0.5) + 32.0); | |
canvas.drawPath( | |
Path() | |
..moveTo(0.0, top) | |
..quadraticBezierTo(0.0, mid, center, mid) | |
..quadraticBezierTo(size.width, mid, size.width, bottom) | |
..lineTo(size.width, -height) | |
..lineTo(0.0, -height) | |
..close(), | |
Paint() | |
..style = PaintingStyle.fill | |
..color = foreground); | |
canvas.restore(); | |
} | |
@override | |
bool shouldRepaint(_CurvePainter oldDelegate) { | |
return height != oldDelegate.height || foreground != oldDelegate.foreground; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment