Here’s a complete Flutter BLoC example showing how to implement a login screen with a dummy API (simulated network call).
This includes:
✅ LoginBloc (handles login events and states)
✅ LoginRepository (fake API call)
✅ LoginScreen (UI with BlocBuilder & BlocListener)
🧱 Folder structure
lib/
├── bloc/
│ ├── login_bloc.dart
│ ├── login_event.dart
│ └── login_state.dart
├── repository/
│ └── login_repository.dart
├── main.dart
└── login_screen.dart
🧩 Step 1: Add Dependencies
In your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
flutter_bloc: ^9.1.1
equatable: ^2.0.5
⚙️ Step 2: Create Repository (dummy API)
// lib/repository/login_repository.dart
import 'dart:async';
class LoginRepository {
Future<bool> login(String email, String password) async {
await Future.delayed(const Duration(seconds: 2)); // simulate network delay
if (email == 'test@example.com' && password == '123456') {
return true; // success
} else {
throw Exception('Invalid credentials');
}
}
}
🧠 Step 3: Create BLoC files
login_event.dart
// lib/bloc/login_event.dart
import 'package:equatable/equatable.dart';
abstract class LoginEvent extends Equatable {
@override
List<Object> get props => [];
}
class LoginButtonPressed extends LoginEvent {
final String email;
final String password;
LoginButtonPressed({required this.email, required this.password});
@override
List<Object> get props => [email, password];
}
login_state.dart
// lib/bloc/login_state.dart
import 'package:equatable/equatable.dart';
abstract class LoginState extends Equatable {
@override
List<Object> get props => [];
}
class LoginInitial extends LoginState {}
class LoginLoading extends LoginState {}
class LoginSuccess extends LoginState {}
class LoginFailure extends LoginState {
final String message;
LoginFailure(this.message);
@override
List<Object> get props => [message];
}
login_bloc.dart
// lib/bloc/login_bloc.dart
import 'package:flutter_bloc/flutter_bloc.dart';
import '../repository/login_repository.dart';
import 'login_event.dart';
import 'login_state.dart';
class LoginBloc extends Bloc<LoginEvent, LoginState> {
final LoginRepository loginRepository;
LoginBloc(this.loginRepository) : super(LoginInitial()) {
on<LoginButtonPressed>(_onLoginButtonPressed);
}
Future<void> _onLoginButtonPressed(
LoginButtonPressed event, Emitter<LoginState> emit) async {
emit(LoginLoading());
try {
bool success = await loginRepository.login(event.email, event.password);
if (success) {
emit(LoginSuccess());
}
} catch (e) {
emit(LoginFailure(e.toString().replaceAll('Exception: ', '')));
}
}
}
🖥️ Step 4: Create UI (login_screen.dart)
// lib/login_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'bloc/login_bloc.dart';
import 'bloc/login_event.dart';
import 'bloc/login_state.dart';
import 'repository/login_repository.dart';
class LoginScreen extends StatelessWidget {
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
LoginScreen({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => LoginBloc(LoginRepository()),
child: Scaffold(
appBar: AppBar(title: const Text('Login')),
body: BlocListener<LoginBloc, LoginState>(
listener: (context, state) {
if (state is LoginSuccess) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Login Successful!')),
);
} else if (state is LoginFailure) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.message)),
);
}
},
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: emailController,
decoration: const InputDecoration(labelText: 'Email'),
),
const SizedBox(height: 10),
TextField(
controller: passwordController,
decoration: const InputDecoration(labelText: 'Password'),
obscureText: true,
),
const SizedBox(height: 20),
BlocBuilder<LoginBloc, LoginState>(
builder: (context, state) {
if (state is LoginLoading) {
return const CircularProgressIndicator();
}
return ElevatedButton(
onPressed: () {
final email = emailController.text.trim();
final password = passwordController.text.trim();
context.read<LoginBloc>().add(
LoginButtonPressed(
email: email, password: password),
);
},
child: const Text('Login'),
);
},
),
],
),
),
),
),
);
}
}
🚀 Step 5: Run App (main.dart)
// lib/main.dart
import 'package:flutter/material.dart';
import 'login_screen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'BLoC Login Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: LoginScreen(),
);
}
}
✅ Test Credentials
Use these to test:
Email: test@example.com
Password: 123456
1 comment
[…] bloc example with dummy api for login screen […]