In this flutter tutorial we are going to create Flutter Login page user Interface. This Login Page UI contains Textfields for user inputs (User name and Password) and created a button widget with Material and ink widgets for user event on submitting the user credentials.
We are going to create below login page
To implement this page we are going to create below pages
App folder structure will be like below
Let's add code to the respected widgets
wave_widget page
import 'package:flutter/material.dart'; import 'dart:math' as Math; import 'package:flutter_login_wave_anim/ui/widgets/clipper_widget.dart'; class WaveWidget extends StatefulWidget { final Size size; final double yOffset; final Color color; WaveWidget({ required this.size, required this.yOffset, required this.color, }); @override _WaveWidgetState createState() => _WaveWidgetState(); } class _WaveWidgetState extends State<WaveWidget> with TickerProviderStateMixin { late AnimationController animationController; List<Offset> wavePoints = []; @override void initState() { super.initState(); animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 5000)) ..addListener(() { wavePoints.clear(); final double waveSpeed = animationController.value * 1080; final double fullSphere = animationController.value * Math.pi * 2; final double normalizer = Math.cos(fullSphere); final double waveWidth = Math.pi / 270; final double waveHeight = 20.0; for (int i = 0; i <= widget.size.width.toInt(); ++i) { double calc = Math.sin((waveSpeed - i) * waveWidth); wavePoints.add( Offset( i.toDouble(), //X calc * waveHeight * normalizer + widget.yOffset, //Y ), ); } }); animationController.repeat(); } @override void dispose() { animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: animationController, builder: (context, _) { return ClipPath( clipper: ClipperWidget( waveList: wavePoints, ), child: Container( width: widget.size.width, height: widget.size.height, color: widget.color, ), ); }, ); } } |
textfield_widget
import 'package:flutter/material.dart'; import 'package:flutter_login_wave_anim/globals.dart'; import 'package:flutter_login_wave_anim/ui/models/home_model.dart'; import 'package:provider/provider.dart'; class TextFieldWidget extends StatelessWidget { final String hintText; final IconData prefixIconData; final IconData suffixIconData; TextInputAction? inputAction=TextInputAction.next; final bool obscureText; Function(String value)? onChanged; TextFieldWidget({ required this.hintText, required this.prefixIconData, required this.suffixIconData, required this.obscureText, this.onChanged, this.inputAction }); @override Widget build(BuildContext context) { final model = Provider.of<HomeModel>(context); return TextField( textInputAction: inputAction, onChanged: onChanged, obscureText: obscureText, cursorColor: Global.themeColor, style: TextStyle( color: Global.themeColor, fontSize: 14.0, ), decoration: InputDecoration( labelStyle: TextStyle(color: Global.themeColor), focusColor: Global.themeColor, filled: true, enabledBorder: UnderlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide.none, ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Global.themeColor), ), labelText: hintText, prefixIcon: Icon( prefixIconData, size: 18, color: Global.themeColor, ), suffixIcon: GestureDetector( onTap: () { model.isVisible = !model.isVisible; }, child: Icon( suffixIconData, size: 18, color: Global.themeColor, ), ), ), ); } } |
clipper_widget
import 'package:flutter/material.dart'; class ClipperWidget extends CustomClipper<Path> { final List<Offset> waveList; ClipperWidget({required this.waveList}); @override getClip(Size size) { final Path path = Path(); path.addPolygon(waveList, false); path.lineTo(size.width, size.height); path.lineTo(0.0, size.height); path.close(); return path; } @override bool shouldReclip(CustomClipper oldClipper) => true; } |
button_widget
import 'package:flutter/material.dart'; import 'package:flutter_login_wave_anim/globals.dart'; class ButtonWidget extends StatelessWidget { final String title; final bool hasBorder; ButtonWidget({ required this.title, required this.hasBorder, }); @override Widget build(BuildContext context) { return Material( child: Ink( decoration: BoxDecoration( color: hasBorder ? Global.white : Global.themeColor, borderRadius: BorderRadius.circular(10), border: hasBorder ? Border.all( color: Global.themeColor, width: 1.0, ) : Border.fromBorderSide(BorderSide.none), ), child: InkWell( borderRadius: BorderRadius.circular(10), child: Container( height: 60.0, child: Center( child: Text( title, style: TextStyle( color: hasBorder ? Global.themeColor : Global.white, fontWeight: FontWeight.w600, fontSize: 16.0, ), ), ), ), ), ), ); } } |
home_model
import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import '/globals.dart'; class HomeModel extends ChangeNotifier { get isVisible => _isVisible; bool _isVisible = false; set isVisible(value) { _isVisible = value; notifyListeners(); } get isValid => _isValid; bool _isValid = false; void isValidEmail(String input) { if (input == Global.validEmail.first) { _isValid = true; } else { _isValid = false; } notifyListeners(); } } |
home_view
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_login_wave_anim/globals.dart'; import 'package:flutter_login_wave_anim/ui/widgets/button_widget.dart'; import 'package:flutter_login_wave_anim/ui/widgets/textfield_widget.dart'; import 'package:flutter_login_wave_anim/ui/widgets/wave_widget.dart'; import 'package:provider/provider.dart'; import 'models/home_model.dart'; class HomeView extends StatelessWidget { @override Widget build(BuildContext context) { final size = MediaQuery.of(context).size; final bool keyboardOpen = MediaQuery.of(context).viewInsets.bottom > 0; final model = Provider.of<HomeModel>(context); return Scaffold( backgroundColor: Global.white, body: Stack( children: <Widget>[ Container( height: size.height - 200, color: Global.themeColor, ), AnimatedPositioned( duration: Duration(milliseconds: 500), curve: Curves.easeOutQuad, top: keyboardOpen ? -size.height / 3.7 : 0.0, child: WaveWidget( size: size, yOffset: size.height / 3.0, color: Global.white, ), ), Padding( padding: const EdgeInsets.only(top: 100.0), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( 'Login', style: TextStyle( color: Global.white, fontSize: 40.0, fontWeight: FontWeight.w900, ), ), ], ), ), Padding( padding: const EdgeInsets.all(30.0), child: Column( mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ TextFieldWidget( inputAction:TextInputAction.next, hintText: 'Email', obscureText: false, prefixIconData: Icons.mail_outline, suffixIconData: model.isValid ? Icons.check : Icons.yard, onChanged: (value) { model.isValidEmail(value); }, ), SizedBox( height: 10.0, ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: <Widget>[ TextFieldWidget( inputAction:TextInputAction.done, hintText: 'Password', obscureText: model.isVisible ? false : true, prefixIconData: Icons.lock_outline, suffixIconData: model.isVisible ? Icons.visibility : Icons.visibility_off, ), SizedBox( height: 10.0, ), Text( 'Forgot password?', style: TextStyle( color: Global.themeColor, ), ), ], ), SizedBox( height: 20.0, ), ButtonWidget( title: 'Login', hasBorder: false, ), SizedBox( height: 10.0, ), ButtonWidget( title: 'Sign Up', hasBorder: true, ), ], ), ), ], ), ); } } |
global
import 'dart:ui'; import 'package:flutter/material.dart'; class Global { static const Color white = const Color(0xffffffff); static const Color themeColor = Colors.green; static const List validEmail = ['test@gmail.com']; } |
main
import 'package:flutter/material.dart'; import 'package:flutter_login_wave_anim/ui/home_view.dart'; import 'package:flutter_login_wave_anim/ui/models/home_model.dart'; import 'package:provider/provider.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return ChangeNotifierProvider( create: (context) => HomeModel(), child: MaterialApp( debugShowCheckedModeBanner: false, home: HomeView(), ), ); } } |
Conclusion: In this flutter login page example we added waves animation to background page.
Article Contributed By :
|
|
|
|
2103 Views |