Animao sign in and sign up
🎌 Penjelasan Kode Flutter Animao App
Panduan lengkap memahami struktur dan komponen aplikasi Animao yang dibuat dengan Flutter
🏗️ Struktur Dasar Aplikasi
- AnimaoApp → Kelas utama yang mengatur tema dan routing aplikasi
- WelcomeScreen → Halaman pembuka dengan background anime dan tombol Sign In/Up
- SignInScreen → Halaman login dengan form email dan password
- SignUpScreen → Halaman registrasi dengan form lengkap
🎨 Komponen Background & Image
📸 Image.network()
Berfungsi untuk menampilkan gambar dari internet sebagai background. Dilengkapi dengan:
- loadingBuilder → Menampilkan loading spinner saat gambar dimuat
- errorBuilder → Menampilkan fallback jika gambar gagal dimuat
- fit: BoxFit.cover → Membuat gambar memenuhi seluruh container
🌫️ BackdropFilter
Memberikan efek blur pada background image dengan ImageFilter.blur(sigmaX: 2.0, sigmaY: 2.0)
🎨 Gradient & Styling
✨ LinearGradient
Digunakan untuk membuat efek gradasi warna pada:
- Background Overlay → Gradient dari putih solid ke transparan
- Tombol → Gradient dari hitam ke ungu (#6B46C1)
- Loading State → Gradient lembut untuk tampilan fallback
🔘 Tombol & Interaksi
🎯 ElevatedButton dengan Gradient
Struktur tombol menggunakan kombinasi Container + ElevatedButton:
- Container → Memberikan gradient background dengan BoxDecoration
- ElevatedButton → Tombol transparan di atas gradient
- BorderRadius.circular(28) → Membuat sudut rounded
- Navigator.push() → Navigasi ke halaman lain saat ditekan
Contoh Tombol Gradient
📝 Form Input & TextField
⌨️ TextField Components
Form input menggunakan TextField dengan styling khusus:
- InputDecoration → Mengatur tampilan field (hint, background, border)
- filled: true → Background berwarna #F5F5F5
- borderSide: BorderSide.none → Menghilangkan border default
- obscureText: true → Menyembunyikan teks untuk password
Contoh TextField dengan styling
📐 Layout & Positioning
- Stack → Menyusun widget bertumpuk (background image + overlay + content)
- Positioned.fill → Membuat widget memenuhi seluruh parent
- SafeArea → Menghindari area notch dan status bar
- Column → Menyusun widget secara vertikal
- Expanded → Membagi ruang yang tersedia secara proporsional
- Padding → Memberikan jarak internal pada container
🎨 Typography & Text Styling
✍️ TextStyle Properties
- fontSize → Ukuran teks (32px untuk heading, 16px untuk body)
- fontWeight → Ketebalan font (bold, w600, normal)
- color → Warna teks (Colors.black87, Colors.black54)
- height → Line height untuk jarak antar baris
Hi there,
Welcome to Animao — your gateway to the world of anime.
🔄 Navigation & State Management
- Navigator.push() → Navigasi ke halaman baru (menambah ke stack)
- Navigator.pushReplacement() → Mengganti halaman saat ini
- Navigator.pop() → Kembali ke halaman sebelumnya
- MaterialPageRoute → Route dengan transisi material design
- StatelessWidget → Widget tanpa state yang dapat berubah
🎯 Fitur Khusus & Optimasi
⚡ Performance & UX
- Loading Builder → Menampilkan progress indicator saat loading
- Error Handling → Fallback UI jika gambar gagal dimuat
- SingleChildScrollView → Membuat halaman dapat di-scroll
- Google Sign In Button → Integrasi dengan akun Google
- Form Validation → Ready untuk validasi input
🎨 Color Palette
#6B46C1
Primary Purple
Primary Purple
#2D1B69
Dark Purple
Dark Purple
#F5F5F5
Input Background
Input Background
#000000
Gradient Start
Gradient Start
💻 Full Flutter Code
import 'package:flutter/material.dart';
import 'dart:ui';
void main() {
runApp(AnimaoApp());
}
class AnimaoApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Animao',
theme: ThemeData(
primarySwatch: Colors.purple,
fontFamily: 'Roboto',
),
home: WelcomeScreen(),
debugShowCheckedModeBanner: false,
);
}
}
class WelcomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: double.infinity,
height: double.infinity,
child: Stack(
children: [
// Full background anime image with blur
Positioned.fill(
child: Stack(
children: [
Image.network(
'https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcQ6SF_68TRFATVjqh0r0E-biPRGmcnauwuDm-5wWWyT3hTOQeRe',
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFFE8E4FF),
Color(0xFFF0ECFF),
Colors.white,
],
),
),
child: Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Color(0xFF6B46C1)),
),
),
);
},
errorBuilder: (context, error, stackTrace) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFFE8E4FF),
Color(0xFFF0ECFF),
Colors.white,
],
),
),
child: Center(
child: Icon(
Icons.image,
color: Color(0xFF6B46C1),
size: 80,
),
),
);
},
),
// Blur effect on the background image
Positioned.fill(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 2.0, sigmaY: 2.0),
child: Container(
color: Colors.transparent,
),
),
),
],
),
),
// White gradient overlay from bottom to top (transparent)
Positioned.fill(
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Colors.white,
Colors.white.withOpacity(0.9),
Colors.white.withOpacity(0.7),
Colors.white.withOpacity(0.4),
Colors.white.withOpacity(0.2),
Colors.transparent,
Colors.transparent,
],
stops: [0.0, 0.2, 0.35, 0.5, 0.65, 0.8, 1.0],
),
),
),
),
// Content
SafeArea(
child: Column(
children: [
Expanded(
flex: 3,
child: Container(),
),
Expanded(
flex: 2,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 32),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Hi there,',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
SizedBox(height: 8),
Text(
'Welcome to Animao — your gateway to the world of anime.',
style: TextStyle(
fontSize: 16,
color: Colors.black54,
height: 1.4,
),
),
SizedBox(height: 8),
Text(
'Discover, explore, and save your favorite anime in one place.',
style: TextStyle(
fontSize: 16,
color: Colors.black54,
height: 1.4,
),
),
Spacer(),
Column(
children: [
Container(
width: double.infinity,
height: 56,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Colors.black,
Color(0xFF2D1B69),
],
),
borderRadius: BorderRadius.circular(28),
),
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SignUpScreen()),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
shadowColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(28),
),
elevation: 0,
),
child: Text(
'Sign Up',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
SizedBox(height: 16),
Container(
width: double.infinity,
height: 56,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Colors.black,
Color(0xFF6B46C1),
],
),
borderRadius: BorderRadius.circular(28),
),
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SignInScreen()),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
shadowColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(28),
),
elevation: 0,
),
child: Text(
'Sign In',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
SizedBox(height: 32),
],
),
],
),
),
),
],
),
),
],
),
),
);
}
}
class SignInScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios, color: Colors.black),
onPressed: () => Navigator.pop(context),
),
),
body: Padding(
padding: EdgeInsets.all(32),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 40),
Text(
'Welcome back,',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.black87,
height: 1.2,
),
),
SizedBox(height: 8),
Text(
'Glad to see you, Again!',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.black87,
height: 1.2,
),
),
SizedBox(height: 40),
TextField(
decoration: InputDecoration(
hintText: 'Email',
hintStyle: TextStyle(color: Colors.grey[500]),
filled: true,
fillColor: Color(0xFFF5F5F5),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide.none,
),
contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 18),
),
),
SizedBox(height: 16),
TextField(
obscureText: true,
decoration: InputDecoration(
hintText: 'Password',
hintStyle: TextStyle(color: Colors.grey[500]),
filled: true,
fillColor: Color(0xFFF5F5F5),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide.none,
),
contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 18),
),
),
SizedBox(height: 32),
Container(
width: double.infinity,
height: 56,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Colors.black,
Color(0xFF6B46C1),
],
),
borderRadius: BorderRadius.circular(28),
),
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
shadowColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(28),
),
elevation: 0,
),
child: Text(
'Sign In',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
SizedBox(height: 32),
Row(
children: [
Expanded(child: Divider(color: Colors.grey[300])),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Text(
'Or Sign In with',
style: TextStyle(color: Colors.grey[600]),
),
),
Expanded(child: Divider(color: Colors.grey[300])),
],
),
SizedBox(height: 32),
Container(
width: double.infinity,
height: 56,
child: OutlinedButton(
onPressed: () {},
style: OutlinedButton.styleFrom(
side: BorderSide(color: Colors.grey[300]!),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(28),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 40,
height: 40,
child: Image.asset(
'assets/google.jpg',
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) {
return Container(
width: 20,
height: 20,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue, Colors.green, Colors.yellow, Colors.red],
),
shape: BoxShape.circle,
),
child: Center(
child: Text(
'G',
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
);
},
),
),
],
),
),
),
Spacer(),
// "Don't have an account? Sign Up" text at the bottom of Sign In screen
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Don't have an account? ",
style: TextStyle(
color: Colors.grey[600],
fontSize: 14,
),
),
GestureDetector(
onTap: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => SignUpScreen()),
);
},
child: Text(
"Sign Up",
style: TextStyle(
color: Color(0xFF6B46C1),
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
],
),
SizedBox(height: 20),
],
),
),
);
}
}
class SignUpScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios, color: Colors.black),
onPressed: () => Navigator.pop(context),
),
),
body: SingleChildScrollView(
padding: EdgeInsets.all(32),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 40),
Text(
'Hello,',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.black87,
height: 1.2,
),
),
SizedBox(height: 8),
Text(
'Sign up to get started',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.black87,
height: 1.2,
),
),
SizedBox(height: 40),
TextField(
decoration: InputDecoration(
hintText: 'Username',
hintStyle: TextStyle(color: Colors.grey[500]),
filled: true,
fillColor: Color(0xFFF5F5F5),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide.none,
),
contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 18),
),
),
SizedBox(height: 16),
TextField(
decoration: InputDecoration(
hintText: 'Email',
hintStyle: TextStyle(color: Colors.grey[500]),
filled: true,
fillColor: Color(0xFFF5F5F5),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide.none,
),
contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 18),
),
),
SizedBox(height: 16),
TextField(
obscureText: true,
decoration: InputDecoration(
hintText: 'Password',
hintStyle: TextStyle(color: Colors.grey[500]),
filled: true,
fillColor: Color(0xFFF5F5F5),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide.none,
),
contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 18),
),
),
SizedBox(height: 16),
TextField(
obscureText: true,
decoration: InputDecoration(
hintText: 'Confirm Password',
hintStyle: TextStyle(color: Colors.grey[500]),
filled: true,
fillColor: Color(0xFFF5F5F5),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide.none,
),
contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 18),
),
),
SizedBox(height: 32),
Container(
width: double.infinity,
height: 56,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Colors.black,
Color(0xFF6B46C1),
],
),
borderRadius: BorderRadius.circular(28),
),
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
shadowColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(28),
),
elevation: 0,
),
child: Text(
'Sign Up',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
SizedBox(height: 32),
Row(
children: [
Expanded(child: Divider(color: Colors.grey[300])),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Text(
'Or Sign Up with',
style: TextStyle(color: Colors.grey[600]),
),
),
Expanded(child: Divider(color: Colors.grey[300])),
],
),
SizedBox(height: 32),
Container(
width: double.infinity,
height: 56,
child: OutlinedButton(
onPressed: () {},
style: OutlinedButton.styleFrom(
side: BorderSide(color: Colors.grey[300]!),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(28),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 40,
height: 40,
child: Image.asset(
'assets/google.jpg',
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) {
return Container(
width: 20,
height: 20,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue, Colors.green, Colors.yellow, Colors.red],
),
shape: BoxShape.circle,
),
child: Center(
child: Text(
'G',
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
);
},
),
),
],
),
),
),
SizedBox(height: 20),
// "Already have an account? Sign In" text at the bottom of Sign Up screen
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Already have an account? ",
style: TextStyle(
color: Colors.grey[600],
fontSize: 14,
),
),
GestureDetector(
onTap: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => SignInScreen()),
);
},
child: Text(
"Sign In",
style: TextStyle(
color: Color(0xFF6B46C1),
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
],
),
SizedBox(height: 20),
],
),
),
);
}
}
🚀 Sekarang Anda memahami struktur lengkap aplikasi Animao Flutter!
Aplikasi ini menggunakan widget modern Flutter dengan design yang clean dan user-friendly
Komentar
Posting Komentar