In this article, we are going to develop a Basic Search App using Flutter Bloc and Clean Architecture. In Flutter applications, the Flutter BLoC (Business Logic Component) is used to manage the state. It helps separate business logic from UI. It ensures that the user interface is not strongly liaison to the business logic, making the app more versatile.
Note: To know more about Flutter- Business Logic Component (BLoC).
Let's see a demo video of what we are going to develop.
Demo Video:
Step-by-Step Implementation
Step 1: Create a new Flutter Application
Create a new Flutter application using the command Prompt. To create a new app, write the following command and run it.
flutter create app_name
To know more about it refer this article: Creating a Simple Application in Flutter
Step 2: Add Dependencies
To add the dependency to the pubspec.yaml file, add flutter_bloc as a dependency in the dependencies part of the pubspec.yaml file, as shown below:
Dart
dependencies:
flutter:
sdk: flutter
flutter_bloc: ^8.1.6
cupertino_icons: ^1.0.6
Now, run the below command in the terminal.
flutter pub get
Or
Run the below command in the terminal.
flutter pub add flutter_bloc
Step 3: Install the Bloc Extension
Install the following bloc extension in VSCode.
Note: If You are not using Vs Code or Don't want to automate the folder structure then, Skip this step and do it manually by creating every file.
It will help you to generate the boilerplate code and folder structure automatically, for that follow the steps below.
- Create a folder
After installing the VSCode extension, create a new folder inside the lib folder and name it Search.
- Select New Bloc
Now, right click on Search folder, it will display an option named 'Bloc: New Bloc', select that option.
- Enter Bloc Name
After selecting New Bloc, it will ask for the name of the Bloc. Enter the New Bloc name as 'Search' and click on Enter!.
Then, it will create a new folder in your Search folder named bloc with all required files as image below.
Step 4: Clean Architecture
Note: If you are not following step3 create a folder structure manually as the image located above the Step 4.
To maintain your codebase clean and interesting, use Clean Architecture. For that, follow the folder structure as mentioned in below image.
Here we created 3 folders and 3 files inside every folder, respectively.
- model: To store all kinds of models, for example result_model.
- result_model.dart: This model file describes how the result of the search is going to be.
- repo: repo is nothing but a repository means it contains important logic files, for example logic file for searching (search_repo).
- search_repo.dart: This repo file has the logic code for searching.
- ui: ui is nothing but User Interface means this folder contains all ui code files in it, for example search_screen file.
- search_screen.dart: This ui file has the UI code.
Step 5: Working with the Search folder
Note: If you did not watched Demo Video, Watch it carefully once again otherwise you won't understand what we are going to discuss.
In the Search folder we have 3 files:
- search_event.dart: Triggers the event from UI and passes the current state, Nothing but user interacting with UI.
- Example: After entering some text on the TextField user will tap on the search icon or submits the TextField by tapping on done in keyboard, then we are triggering the SearchEventLoadData event and passing that input (Ex: hi, food in demo video) as state.
- search_bloc.dart: Catches the triggered event with the state (Ex:hi, food in demo video) and updates the state.
- Example: After catching the triggered event with the state (Ex:hi, food in demo video), it passes that input (state) to repo, and when it gives the result then it emits that through SearchStateLoaded.
- search_state.dart: Used to emit the state in search_bloc.dart and catch the state and categorize, and display UI according to that.
- Example: After updating the state in search_bloc.dart it throws the state to UI, then it is categorizes and showing the result as Result for "hi".. or Result for "food".
- search_event.dart:
search_event.dart
import 'package:flutter/material.dart';
import 'package:flutter_geeks/Search/model/result_model.dart';
/// Base class for all search events.
/// This class is immutable and sealed, meaning no other classes can extend it
/// outside of this file.
@immutable
sealed class SearchEvent {}
/// Event to load data for the search functionality.
/// Contains a list of strings (`strList`) and a list of `ResultModel` objects (`dataList`).
class SearchEventLoadData extends SearchEvent {
// List of strings to be used in the search.
final List<String> strList;
// List of data models for the search results.
final List<ResultModel> dataList;
/// Constructor for initializing the `SearchEventLoadData` event.
/// Both `strList` and `dataList` are required.
SearchEventLoadData({required this.strList, required this.dataList});
List<Object> get props => [strList, dataList];
}
- search_state.dart:
search_state.dart
import 'package:flutter/material.dart';
import 'package:flutter_geeks/Search/model/result_model.dart';
/// Base class for all search states, marked as immutable.
@immutable
sealed class SearchState {}
/// Initial state of the search, before any action is taken.
final class SearchInitial extends SearchState {}
/// State representing that the search has not been loaded yet.
class SearchStateNotLoaded extends SearchState {}
/// State representing that the search is currently loading.
class SearchStateLoading extends SearchState {}
/// State representing that the search has successfully loaded data.
class SearchStateLoaded extends SearchState {
// List of strings related to the search.
final List<String> strList;
// List of result models from the search.
final List<ResultModel> dataList;
SearchStateLoaded(this.strList, this.dataList);
List<Object> get props => [strList, dataList];
}
/// State representing an error that occurred during the search process.
class SearchStateError extends SearchState {
// Error message describing the issue.
final String message;
SearchStateError(this.message);
List<Object> get props => [message];
}
- search_bloc.dart:
search_bloc.dart
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_geeks/Search/bloc/search_event.dart';
import 'package:flutter_geeks/Search/bloc/search_state.dart';
import 'package:flutter_geeks/Search/repo/search_repo.dart';
/// Bloc class to handle search-related events and states
class SearchBloc extends Bloc<SearchEvent, SearchState> {
// Repository to fetch search details
final SearchRepo _searchRepo;
/// Constructor to initialize the SearchBloc with the repository
SearchBloc(this._searchRepo) : super(SearchStateNotLoaded()) {
// Registering the event handler for SearchEventLoadData
on<SearchEventLoadData>(_onLoadData);
}
/// Event handler for loading data
///
/// Emits:
/// - [SearchStateLoading] while data is being fetched
/// - [SearchStateLoaded] when data is successfully fetched
/// - [SearchStateError] if an error occurs during data fetching
Future<void> _onLoadData(
SearchEventLoadData event,
Emitter<SearchState> emit,
) async {
// Emit loading state
emit(SearchStateLoading());
try {
// Fetch the details from the repository using the last string in the list
final result = await _searchRepo.getDetails(event.strList.last);
// Add the fetched result to the data list
event.dataList.add(result);
// Emit the loaded state with updated lists
emit(SearchStateLoaded(event.strList, event.dataList));
} catch (e) {
// Emit an error state if an exception occurs
emit(SearchStateError(e.toString()));
}
}
}
Step 6: Working with the model
This file is used to represent the result data and the reason, why you are seeing the result as title and description in the demo video.
- result_model.dart:
Dart
/// A model class representing the result data.
class ResultModel {
/// The title of the result.
final String title;
/// The description of the result.
final String description;
/// Constructor for creating a [ResultModel] instance.
///
/// Both [title] and [description] are required.
ResultModel({required this.title, required this.description});
/// Factory constructor to create a [ResultModel] instance from a JSON map.
///
/// Expects the JSON map to have keys 'title' and 'description'.
factory ResultModel.fromJson(Map<String, dynamic> json) {
return ResultModel(
title: json['title'], // Extracts the title from the JSON map.
description: json['description'], // Extracts the description from the JSON map.
);
}
}
Step 7: Working with repo
This file contains the Logic for Search, which we are using to get data for updating the state.
- search_repo.dart:
search_repo.dart
import '../model/result_model.dart';
// Repository class to handle search-related operations
class SearchRepo {
// Message to store error or status information
String message = '';
// Method to fetch details based on a query
// Simulates an API call and returns a ResultModel object
Future<ResultModel> getDetails(String query) async {
try {
// Simulate API Call (replace with actual HTTP logic)
await Future.delayed(Duration(seconds: 1)); // Simulated delay for API response
return ResultModel(
title: 'Result for "$query"', // Title of the result
description: 'Detailed information about "$query"', // Description of the result
);
} catch (e) {
// Handle any errors during the API call
message = 'Failed to fetch data'; // Set error message
throw Exception(message); // Throw an exception with the error message
}
}
}
Step 7: Working with the main
Add the boilerplate code below in main.dart to initialize the BlocProvider in the main file and create a basic structure with an MaterialApp.
-BlocProvider: Wrap the home class SearchScreen() with BlocProvider, it is used to initialize the state, i.e, in UI it is showing a 'Search Something..' text in the center.
Dart
home: BlocProvider(
// Creates and provides the SearchBloc
create: (_) => SearchBloc(SearchRepo()),
// Child widget that consumes the bloc
child: SearchScreen(),
),
- main.dart:
Dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_geeks/Search/bloc/search_bloc.dart';
import 'package:flutter_geeks/Search/repo/search_repo.dart';
import 'package:flutter_geeks/Search/ui/search_screen.dart';
// Entry point of the Flutter application
void main() {
runApp(SearchWrapper());
}
// Wrapper widget to provide the SearchBloc to the SearchScreen
class SearchWrapper extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: BlocProvider(
// Creates and provides the SearchBloc
create: (_) => SearchBloc(SearchRepo()),
// Child widget that consumes the bloc
child: SearchScreen(),
),
);
}
}
Step 8: Working with ui
The ui folder contains only one file, search_screen.dart and it contains basics widgets like Scaffold, AppBar, TextFiled, Text, icon. But, to handle ui with respect to state. Follow the below process.
- Initialization: Initialize required variables.
Dart
// Controller for the search input field
final TextEditingController _controller = TextEditingController();
// List to store search history
List<String> searchHistory = [];
// List to store search results
List<ResultModel> resultHistory = [];
- TextField: Used to take input from the user and when user taps on search icon or submits the textfield then calls _onSearch() method.
Dart
// Search input field
TextField(
// Attach the controller
controller: _controller,
onSubmitted: (_) => _onSearch(), // Trigger search on submit
decoration: InputDecoration(
hintText: 'Search...',
suffixIcon: IconButton(
icon: Icon(Icons.search),
onPressed: _onSearch, // Trigger search on icon press
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.black54),
),
),
),
-_onSearch(): Used to trigger the SearchEventLoadData event.
Dart
// Method to handle search action
void _onSearch() {
// Get the trimmed input text
final query = _controller.text.trim();
// Do nothing if the input is empty
if (query.isEmpty) return;
// Add the query to search history
searchHistory.add(query);
// Trigger the SearchBloc to load data
BlocProvider.of<SearchBloc>(context,)
.add(SearchEventLoadData(strList: searchHistory, dataList: resultHistory));
// Clear the input field after search
_controller.clear();
}
- BlocBuilder: Used to catch the updated state and show UI according to that.
Dart
BlocBuilder<SearchBloc, SearchState>(
builder: (context, state) {
if (state is SearchStateLoading) {
// Show loading indicator while data is being fetched
return Center(
child: CircularProgressIndicator(color: Colors.black38),
);
} else if (state is SearchStateLoaded) {
// Display the latest search result
final result = state.dataList.last;
return ListView(
children: [
ListTile(
// Title of the result
title: Text(result.title),
// Description of the result
subtitle: Text(result.description),
),
],
);
} else if (state is SearchStateError) {
// Display error message if an error occurs
return Center(child: Text('Error: ${state.message}'));
} else if (state is SearchStateNotLoaded) {
// Default message when no search has been performed
return Center(child: Text("Search something!"));
}
else{
// Default message when no state matches
return Center(child: Text("No results found!"));
}
},
),
Full UI Code:
Search_screen.dart:
Dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_geeks/Search/bloc/search_event.dart';
import 'package:flutter_geeks/Search/bloc/search_state.dart';
import 'package:flutter_geeks/Search/model/result_model.dart';
import '../bloc/search_bloc.dart';
// Main SearchScreen widget
class SearchScreen extends StatefulWidget {
@override
_SearchScreenState createState() => _SearchScreenState();
}
// State class for SearchScreen
class _SearchScreenState extends State<SearchScreen> {
// Controller for the search input field
final TextEditingController _controller = TextEditingController();
// List to store search history
List<String> searchHistory = [];
// List to store search results
List<ResultModel> resultHistory = [];
// Method to handle search action
void _onSearch() {
// Get the trimmed input text
final query = _controller.text.trim();
// Do nothing if the input is empty
if (query.isEmpty) return;
// Add the query to search history
searchHistory.add(query);
// Trigger the SearchBloc to load data
BlocProvider.of<SearchBloc>(
context,
).add(SearchEventLoadData(strList: searchHistory, dataList: resultHistory));
// Clear the input field after search
_controller.clear();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Search UI"),
backgroundColor: Colors.black38,
foregroundColor: Colors.white,
),
body: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: [
// Search input field
TextField(
// Attach the controller
controller: _controller,
onSubmitted: (_) => _onSearch(), // Trigger search on submit
decoration: InputDecoration(
hintText: 'Search...',
suffixIcon: IconButton(
icon: Icon(Icons.search),
onPressed: _onSearch, // Trigger search on icon press
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.black54),
),
),
),
const SizedBox(height: 20), // Spacing between input and results
// BlocBuilder to handle state changes in SearchBloc
Expanded(
child: BlocBuilder<SearchBloc, SearchState>(
builder: (context, state) {
if (state is SearchStateLoading) {
// Show loading indicator while data is being fetched
return Center(
child: CircularProgressIndicator(color: Colors.black38),
);
} else if (state is SearchStateLoaded) {
// Display the latest search result
final result = state.dataList.last;
return ListView(
children: [
ListTile(
// Title of the result
title: Text(result.title),
// Description of the result
subtitle: Text(result.description),
),
],
);
} else if (state is SearchStateError) {
// Display error message if an error occurs
return Center(child: Text('Error: ${state.message}'));
} else if (state is SearchStateNotLoaded) {
// Default message when no search has been performed
return Center(child: Text("Search something!"));
}
else{
// Default message when no state matches
return Center(child: Text("No results found!"));
}
},
),
),
],
),
),
);
}
}
Program Flow:
When the App started with the help of BlocProvider, it is showing 'search something..'.
BlocProvider(
create: (context) => SearchBloc(SearchRepo()),
.....
)
As we are calling SearchBloc() class in search_bloc.dart and that calling SearchStateNotLoaded( ):
SearchBloc() : (this._searchRepo) : super(SearchStateNotLoaded()) {
...
}
When it comes to UI, after that, BlocBuilder catches that state, i.e, it will display 'search something..'. At the start of the app.
BlocBuilder<SearchBloc, SearchState>(
builder: (context, state) {
if (state is SearchStateLoading) {
// Code
} else if (state is SearchStateLoaded) {
// Code
} else if (state is SearchStateError) {
// Code
} else if (state is SearchStateNotLoaded) {
// Default message when no search has been performed
return Center(child: Text("Search something!"));
}
else{
// Code
}
},
),
When the user enters something and taps on the search icon or submits the textfield, it triggers the event with the help of _onSearch() method.
TextField(
// Attach the controller
controller: _controller,
onSubmitted: (_) => _onSearch(), // Trigger search on submit
decoration: InputDecoration(
hintText: 'Search...',
suffixIcon: IconButton(
icon: Icon(Icons.search),
onPressed: _onSearch, // Trigger search on icon press
),
),
),
// Method to handle search action
void _onSearch() {
......
BlocProvider.of<SearchBloc>(
context,
).add(SearchEventLoadData(strList: searchHistory, dataList: resultHistory));
......
}
Then it is taking the current state like searchHistory and resultHistory value and going to search_bloc.dart and searches for the right on catch(Ex: here on<SearchEventLoadData>(_onLoadData); ),
on<SearchEventLoadData>(_onLoadData);
Future<void> _onLoadData(
SearchEventLoadData event,
Emitter<SearchState> emit,
) async {
// Emit loading state
emit(SearchStateLoading());
try {
// Fetch the details from the repository using the last string in the list
final result = await _searchRepo.getDetails(event.strList.last);
// Add the fetched result to the data list
event.dataList.add(result);
// Emit the loaded state with updated lists
emit(SearchStateLoaded(event.strList, event.dataList));
} catch (e) {
// Emit an error state if an exception occurs
emit(SearchStateError(e.toString()));
}
}
if it finds then it will emit the emit(SearchStateLoading()); , then it will come to the UI file search_screen.dart categorizes the state, and because of it, we can see the circular progress bar first.
emit(SearchStateLoading());
if (state is SearchStateLoading) {
// Show loading indicator while data is being fetched
return Center(
child: CircularProgressIndicator(color: Colors.black38),
);
}
And then it's calling the getDetails method with event.strList.last as previous state in SearchRepo, and that returning below result Structured with the result model which is in result_model.dart.
final result = await _searchRepo.getDetails(event.strList.last);
import '../model/result_model.dart';
// Repository class to handle search-related operations
class SearchRepo {
.....
Future<ResultModel> getDetails(String query) async {
try {
// Simulate API Call (replace with actual HTTP logic)
await Future.delayed(Duration(seconds: 1)); // Simulated delay for API response
return ResultModel(
title: 'Result for "$query"', // Title of the result
description: 'Detailed information about "$query"', // Description of the result
);
} catch (e) {
.....
}
}
}
And, we are getting the updated state as this Result, adding that to the event and emitting that updated state to SearchStateLoaded.
// Add the fetched result to the data list
event.dataList.add(result);
// Emit the loaded state with updated lists
emit(SearchStateLoaded(event.strList, event.dataList));
Then, BlocBuilder in the main.dart catching that updated state and displaying it in the UI.
BlocBuilder<SearchBloc, SearchState>(
builder: (context, state) {
if (state is SearchStateLoading) {
...
} else if (state is SearchStateLoaded) {
// Display the latest search result
final result = state.dataList.last;
return ListView(
children: [
ListTile(
// Title of the result
title: Text(result.title),
// Description of the result
subtitle: Text(result.description),
),
],
);
} else if (state is SearchStateError) {
...
} else if (state is SearchStateNotLoaded) {
....
}
else{
.....
}
},
),
This is what is happening in the whole code.
Complete Source Code:
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_geeks/Search/bloc/search_bloc.dart';
import 'package:flutter_geeks/Search/repo/search_repo.dart';
import 'package:flutter_geeks/Search/ui/search_screen.dart';
// Entry point of the Flutter application
void main() {
runApp(SearchWrapper());
}
// Wrapper widget to provide the SearchBloc to the SearchScreen
class SearchWrapper extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: BlocProvider(
// Creates and provides the SearchBloc
create: (_) => SearchBloc(SearchRepo()),
// Child widget that consumes the bloc
child: SearchScreen(),
),
);
}
}
ui/search_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_geeks/Search/bloc/search_event.dart';
import 'package:flutter_geeks/Search/bloc/search_state.dart';
import 'package:flutter_geeks/Search/model/result_model.dart';
import '../bloc/search_bloc.dart';
// Main SearchScreen widget
class SearchScreen extends StatefulWidget {
@override
_SearchScreenState createState() => _SearchScreenState();
}
// State class for SearchScreen
class _SearchScreenState extends State<SearchScreen> {
// Controller for the search input field
final TextEditingController _controller = TextEditingController();
// List to store search history
List<String> searchHistory = [];
// List to store search results
List<ResultModel> resultHistory = [];
// Method to handle search action
void _onSearch() {
// Get the trimmed input text
final query = _controller.text.trim();
// Do nothing if the input is empty
if (query.isEmpty) return;
// Add the query to search history
searchHistory.add(query);
// Trigger the SearchBloc to load data
BlocProvider.of<SearchBloc>(
context,
).add(SearchEventLoadData(strList: searchHistory, dataList: resultHistory));
// Clear the input field after search
_controller.clear();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Search UI"),
backgroundColor: Colors.black38,
foregroundColor: Colors.white,
),
body: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: [
// Search input field
TextField(
// Attach the controller
controller: _controller,
onSubmitted: (_) => _onSearch(), // Trigger search on submit
decoration: InputDecoration(
hintText: 'Search...',
suffixIcon: IconButton(
icon: Icon(Icons.search),
onPressed: _onSearch, // Trigger search on icon press
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.black54),
),
),
),
const SizedBox(height: 20), // Spacing between input and results
// BlocBuilder to handle state changes in SearchBloc
Expanded(
child: BlocBuilder<SearchBloc, SearchState>(
builder: (context, state) {
if (state is SearchStateLoading) {
// Show loading indicator while data is being fetched
return Center(
child: CircularProgressIndicator(color: Colors.black38),
);
} else if (state is SearchStateLoaded) {
// Display the latest search result
final result = state.dataList.last;
return ListView(
children: [
ListTile(
// Title of the result
title: Text(result.title),
// Description of the result
subtitle: Text(result.description),
),
],
);
} else if (state is SearchStateError) {
// Display error message if an error occurs
return Center(child: Text('Error: ${state.message}'));
} else if (state is SearchStateNotLoaded) {
// Default message when no search has been performed
return Center(child: Text("Search something!"));
}
else{
// Default message when no state matches
return Center(child: Text("No results found!"));
}
},
),
),
],
),
),
);
}
}
bloc/search_bloc.dart
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_geeks/Search/bloc/search_event.dart';
import 'package:flutter_geeks/Search/bloc/search_state.dart';
import 'package:flutter_geeks/Search/repo/search_repo.dart';
/// Bloc class to handle search-related events and states
class SearchBloc extends Bloc<SearchEvent, SearchState> {
// Repository to fetch search details
final SearchRepo _searchRepo;
/// Constructor to initialize the SearchBloc with the repository
SearchBloc(this._searchRepo) : super(SearchStateNotLoaded()) {
// Registering the event handler for SearchEventLoadData
on<SearchEventLoadData>(_onLoadData);
}
/// Event handler for loading data
///
/// Emits:
/// - [SearchStateLoading] while data is being fetched
/// - [SearchStateLoaded] when data is successfully fetched
/// - [SearchStateError] if an error occurs during data fetching
Future<void> _onLoadData(
SearchEventLoadData event,
Emitter<SearchState> emit,
) async {
// Emit loading state
emit(SearchStateLoading());
try {
// Fetch the details from the repository using the last string in the list
final result = await _searchRepo.getDetails(event.strList.last);
// Add the fetched result to the data list
event.dataList.add(result);
// Emit the loaded state with updated lists
emit(SearchStateLoaded(event.strList, event.dataList));
} catch (e) {
// Emit an error state if an exception occurs
emit(SearchStateError(e.toString()));
}
}
}
bloc/search_event.dart
import 'package:flutter/material.dart';
import 'package:flutter_geeks/Search/model/result_model.dart';
/// Base class for all search events.
/// This class is immutable and sealed, meaning no other classes can extend it
/// outside of this file.
@immutable
sealed class SearchEvent {}
/// Event to load data for the search functionality.
/// Contains a list of strings (`strList`) and a list of `ResultModel` objects (`dataList`).
class SearchEventLoadData extends SearchEvent {
// List of strings to be used in the search.
final List<String> strList;
// List of data models for the search results.
final List<ResultModel> dataList;
/// Constructor for initializing the `SearchEventLoadData` event.
/// Both `strList` and `dataList` are required.
SearchEventLoadData({required this.strList, required this.dataList});
List<Object> get props => [strList, dataList];
}
bloc/search_state.dart
import 'package:flutter/material.dart';
import 'package:flutter_geeks/Search/model/result_model.dart';
/// Base class for all search states, marked as immutable.
@immutable
sealed class SearchState {}
/// Initial state of the search, before any action is taken.
final class SearchInitial extends SearchState {}
/// State representing that the search has not been loaded yet.
class SearchStateNotLoaded extends SearchState {}
/// State representing that the search is currently loading.
class SearchStateLoading extends SearchState {}
/// State representing that the search has successfully loaded data.
class SearchStateLoaded extends SearchState {
// List of strings related to the search.
final List<String> strList;
// List of result models from the search.
final List<ResultModel> dataList;
SearchStateLoaded(this.strList, this.dataList);
List<Object> get props => [strList, dataList];
}
/// State representing an error that occurred during the search process.
class SearchStateError extends SearchState {
// Error message describing the issue.
final String message;
SearchStateError(this.message);
List<Object> get props => [message];
}
repo/search_repo.dart
import '../model/result_model.dart';
// Repository class to handle search-related operations
class SearchRepo {
// Message to store error or status information
String message = '';
// Method to fetch details based on a query
// Simulates an API call and returns a ResultModel object
Future<ResultModel> getDetails(String query) async {
try {
// Simulate API Call (replace with actual HTTP logic)
await Future.delayed(Duration(seconds: 1)); // Simulated delay for API response
return ResultModel(
title: 'Result for "$query"', // Title of the result
description: 'Detailed information about "$query"', // Description of the result
);
} catch (e) {
// Handle any errors during the API call
message = 'Failed to fetch data'; // Set error message
throw Exception(message); // Throw an exception with the error message
}
}
}
model/result_model.dart
/// A model class representing the result data.
class ResultModel {
/// The title of the result.
final String title;
/// The description of the result.
final String description;
/// Constructor for creating a [ResultModel] instance.
///
/// Both [title] and [description] are required.
ResultModel({required this.title, required this.description});
/// Factory constructor to create a [ResultModel] instance from a JSON map.
///
/// Expects the JSON map to have keys 'title' and 'description'.
factory ResultModel.fromJson(Map<String, dynamic> json) {
return ResultModel(
title: json['title'], // Extracts the title from the JSON map.
description: json['description'], // Extracts the description from the JSON map.
);
}
}
Output:
Similar Reads
Basics
Flutter TutorialThis Flutter Tutorial is specifically designed for beginners and experienced professionals. It covers both the basics and advanced concepts of the Flutter framework.Flutter is Googleâs mobile SDK that builds native Android and iOS apps from a single codebase. It was developed in December 2017. When
7 min read
Flutter | An introduction to the open source SDK by GoogleFlutter is Googleâs Mobile SDK to build native iOS and Android, Desktop (Windows, Linux, macOS), and Web apps from a single codebase. When building applications with Flutter, everything is Widgets â the blocks with which the flutter apps are built. They are structural elements that ship with a bunch
5 min read
Flutter - Architecture ApplicationFlutter architecture application mainly consists of: WidgetsGesturesConcept of StateLayersWidgetsWidgets are the primary component of any flutter application. It acts as a UI for the user to interact with the application. Any flutter application is itself a widget that is made up of a combination of
3 min read
Android Studio Setup for Flutter DevelopmentThis article will show how to set up Android Studio to run Flutter Applications. Android Studio is one of the popular IDE( integrated development environment  ) developed by Google itself to create cross-platform Android applications. First, you have to install Android Studio version 3.0 or later, a
3 min read
Getting Started with Cross-Platform Mobile Application using FlutterFlutter is an open-source mobile application development SDK created by Google to develop cross-platform mobile applications. Flutter makes it extremely easy and fast for even novice programmers (computer programmers who are not experienced in any programming languages) to build high-quality and res
7 min read
Flutter Development in Ubuntu 20.04In this article, let's look at how you can set up a development environment for Flutter if you're using Ubuntu 20.04. It was difficult earlier and was kind of a nightmare to get it done. But now, it has changed, and anyone can easily set up a flutter development environment on their Ubuntu system in
5 min read
Key Widgets
UI Components
Flutter - TabsTabs are the part of the UI that navigates the user through different routes(ie, pages) when clicked upon. The use of tabs in applications is standard practice. Flutter provides a simple way to create tab layouts using the material library. In this article, we will be exploring the same in detail.To
2 min read
Flutter - Horizontal ListIn Flutter there can be Two types of lists, namely, horizontal list and vertical list. Both these lists are created using the ListView constructor and assigning the scrollDirection parameter. By default, the scroll direction parameter is vertical for a vertical list but it can be overridden by passi
3 min read
Flutter - Expansion Tile CardThe Expansion Tile Card works similarly to that of the Flutter SDK's standard expansion tile. But it uses the style used by Google itself in its products to raise a tile. It can be called a better version of the Flutter's ExpansionTileCard. In this article, we will look into the process of implement
3 min read
Icon Class in FlutterIcon class in Flutter is used to show specific icons in our app. Instead of creating an image for our icon, we can simply use the Icon class for inserting an icon in our app. For using this class you must ensure that you have set uses-material-design: true in the pubsec.yaml file of your object.Synt
2 min read
Expanded Class in FlutterWhen we create any child of a row or column we provide the size of the widget according to the screen size but sometimes when we provide more size of child as compared to screen size we get a warning and our widget goes out of the screen for resolving this we put a child of a row or column in an exp
3 min read
Flutter - DialogsThe dialog is a type of widget which comes on the window or the screen which contains any critical information or can ask for any decision. When a dialog box is popped up all the other functions get disabled until you close the dialog box or provide an answer. We use a dialog box for a different typ
5 min read
Flutter - Circular & Linear Progress IndicatorsProgress Indicator in any application displays the time which is needed for some tasks to complete such as downloading, installation, uploading, file transfer, etc. This shows the progress of a task or the time to display the length of the processes.In Flutter, progress can be displayed in two ways:
4 min read
Flutter - Staggered Grid ViewStaggered Grid View is a type of layout that is used to display images and posts. As you see in various social platforms such as Pinterest, Instagram and many more. The main feature of Staggered Grid View is that it makes the layout beautiful and develop a great User Experience. Staggered Grid View
4 min read
Design & Animations
Customizing Fonts in FlutterCustomization is everywhere, from documents to apps, we can customize everything as we want to. The power of customization is humongous, and it has revolutionized the way we look at technology in this world. Just like how printing "Hello World", is the basic step towards learning a new programming l
4 min read
Flutter - Skeleton TextIn Flutter, the skeleton_text library is used to easily implement skeleton text loading animation. Its main application in a flutter app is to assure its users that the servers are working but running slow, but the content will eventually load. It also enhances the User Interface if the user connect
3 min read
Flutter - ThemesThemes are an integral part of UI for any application. Themes are used to design the fonts and colors of an application to make it more presentable. In Flutter, the Theme widget is used to add themes to an application. One can use it either for a particular part of the application like buttons and n
3 min read
Flutter - Lazy LoaderThe Lazy loader is a wrapper to the ScrollView that enables lazy loading. It is very useful in situations where the application's intent is to show endless content in a ListView. For instance, Instagram, Facebook, Youtube and most social networking platforms use them to deliver an endless stream of
5 min read
Flutter - UI OrientationAll applications should be able to adjust their User Interface (UI) based on the orientation of the phone. By orientation, we implicate the portrait and landscape mode in smartphones rather the physical orientation. In, Flutter it is done so by using the OrientationBuilder, which determines the app'
2 min read
Flutter - Animation in Route TransitionRoutes are simply Pages in Flutter applications. An application often needs to move from one page to another. However, animations can be used to make these transitions smoother. These animations can be used to curve or tween the Animation object of the PageRouteBuilder class to alter the transition
3 min read
Flutter - Physics Simulation in AnimationPhysics simulation in Flutter is a beautiful way to Animate components of the flutter app to make it look more realistic and interactive. These can be used to create a range of animations like falling objects due to gravity to making a container seem attached to a spring. In this article, we will ex
4 min read
Flutter - Radial Hero AnimationA Radial transformation means turning a circular shape into a square shape. In Radial Hero animation the same is done with a Hero. In Flutter, a Hero Widget is used to create a hero animation. A hero in this context refers to a widget that moves in between screens. This is one of the most fundamenta
3 min read
Flutter - Hinge AnimationAnimations are a big part of the Flutter application. It makes an app sweet to the eyes and equally user-friendly. In this article, we will discuss in detail the Hinge animations. In Flutter there are two ways to work with animations namely:A pub packageAnimated Builder WidgetIn this article, we wil
4 min read
Flutter - Lottie AnimationVisualization is an integral part of any application. Animations can highly glorify the UI of an app, but animations can be hectic to implement for an application. This is where the Lottie animation comes in. Lottie is a JSON-based animation file. It can be used both as a network asset and a static
3 min read
Forms & Gestures
Navigation & Routing
URLs in FlutterWhile surfing the net, every user will come across many buttons, text, etc., that would redirect the user to a different webpage in the same tab or in a different tab when clicked. In the same way, when a developer links a URL to a button or text in an app, the app will open the website when clicked
5 min read
Routes and Navigator in FlutterRoute: Apps are the new trend. The number of apps available in the Play Store and App Store nowadays is quite a lot. The apps display their content in a full-screen container called pages or screens. In flutter, the pages or screens are called Routes. In android, these pages/screens are referred to
4 min read
Flutter - WebSocketsWebSockets are used to connect with the server just like the http package. It supports two-way communication with a server without polling.In this article, we will explore the below-listed topics related to WebSockets in Flutter:Connecting to a WebSocket serverListen to messages from the server.Send
3 min read
Flutter - Named RoutesAn app has to display multiple screens depending upon the user's needs. A user needs to back and forth from the multiple screens to the home screen. In, Flutter this is done with the help of Navigator.Note: In Flutter, screens and pages are called routes. Steps to Implement Named Routes in FlutterSt
3 min read
Flutter - Arguments in Named RoutesNavigating between the various routes (i.e, pages) of an application in Flutter is done with the use of Navigator. The Navigator uses a common identifier to transition between routes. One can pass arguments to these routes using the arguments parameter of Navigator.pushNamed() method. Arguments can
4 min read
Multi Page Applications in FlutterApps are widely used by humans in this techie world. The number of apps in the app store is increasing day by day. Due to this competition, app developers have started to add a number of features to their apps. To reduce this complexity, the content of the app is mostly divided into various pages so
5 min read
Flutter - Updating Data on the InternetIn today's world, most applications heavily rely on fetching and updating information from the servers through the internet. In Flutter, such services are provided by the http package. In this article, we will explore the same. Let's see a sample video of what we are going to develop.Sample Video:St
5 min read
Flutter - Fetching Data From the InternetIn today's world, most applications heavily rely on fetching information from the servers through the internet. In Flutter, such services are provided by the http package. In this article, we will explore this concept.Steps to Implement Data Fetching from the InternetStep 1 : Create a new flutter ap
4 min read
Flutter - Deleting Data On The InternetIn this article, we will explore the process of deleting data on the internet. Before deleting data on the internet, we will fetch the data first and then will delete the data.Steps to implement Deleting Data on the InternetStep 1 : Create a new flutter applicationCreate a new Flutter application us
4 min read
Flutter - Sending Data To The InternetInteracting with the Internet is crucial for most apps to function. In Flutter, sending data to the internet is one of them, and the http package is used to send the data to the internet. In this article, we will explore the same topic in detail. Steps to Implement Sending Data to the InternetStep 1
5 min read
Flutter - Send Data to ScreenInteraction with the UI is an integral part of any application. But more often than not, the information needs to be sent from one screen to another. For instance, say you need to pass data regarding a selected or tapped component of the UI to another route(i.e., page). In this article, we will expl
4 min read
Hardware Interaction
Gallery Access and Camera in FlutterWe can add images from the gallery using the image_picker package in Flutter. For this, you'll need to use your real device.Steps to Implement Gallery and Camera AccessStep 1: Create a new flutter applicationCreate a new Flutter application using the command Prompt. To create a new app, write the be
3 min read
Camera Access in FlutterTo add images from the camera in Flutter, we'll use the image_picker package. For this, you'll need to use your real device.Follow the below steps to display the images from the cameraStep 1: Create a new Flutter ApplicationCreate a new Flutter application using the command Prompt. To create a new a
3 min read
Background local notifications in FlutterSometimes user wants some essential features like the latest news updates, latest articles updates, weather condition alert, complete his/her profile update, future events update and much more in his/ her Android or iOS device without opening App and if you would like to develop this kind of Android
6 min read
Restrict Landscape mode in FlutterIn this article, we'll learn how to restrict landscape mode in the flutter app. A production-ready app should be free from all sorts of bugs and errors. Mostly, we design our app for portrait orientation if we flip to landscape orientation, the UI might not be adjusted for that. So, there are two ca
2 min read
Sample Flutter Apps
Basic Quiz App In FlutterFlutter SDK is an open-source software development kit for building beautiful UI that is natively compiled. Currently, it is available as a stable version for iOS and Android OS. In this app, we are going to have the features or modules mentioned below:Five multiple-choice questions ( more questions
8 min read
A Hello World App using FlutterIn Flutter, everything is a Widget, and by using predefined widgets, one can create user-defined widgets just like using int, float, and double can create user-defined data types. In Flutter there are three types of widgetsStateless WidgetStateful WidgetInherited WidgetIn this article, we will use S
3 min read
Flutter - Simple PDF Generating AppFlutter Community has created several packages to work with PDFs in our apps. In this article, we will be creating a simple PDF-generating app. This application will convert plain text to PDF. The packages that we are going to need are listed below with their uses:pdf: It is a PDF creation library f
9 min read
Flutter - Magic 8 Ball AppWe will be building a Magic 8-Ball app that will give you the answers to all fun, tricky questions (basically, it is a fun game app that will change its state of image using Textbutton in Stateful widgets). For this, we will use Stateless and Stateful Flutter widgets and a Textbutton. Steps to build
3 min read
Advance Concepts