Business logic components (BLoC) allow you to separate the business logic from the UI. Using Bloc, we can easily separate the application into multiple layers, first we would have the presentation layer which would contain the UI/Views/widgets, then the business logic layer (Bloc) which will take care about the state changes and will have a dependency on the data access layer which contains a repository class which will act as an abstract class above the data access object classes.
class Recipe {
Recipe({
required this.publisher,
required this.title,
required this.sourceUrl,
required this.recipeId,
required this.imageUrl,
required this.socialRank,
required this.publisherUrl,
});
String publisher;
String title;
String sourceUrl;
String recipeId;
String imageUrl;
double socialRank;
String publisherUrl;
factory Recipe.fromJson(Map<String, dynamic> json) => Recipe(
publisher: json["publisher"],
title: json["title"],
sourceUrl: json["source_url"],
recipeId: json["recipe_id"],
imageUrl: json["image_url"],
socialRank: json["social_rank"]?.toDouble(),
publisherUrl: json["publisher_url"],
);
Map<String, dynamic> toJson() => {
"publisher": publisher,
"title": title,
"source_url": sourceUrl,
"recipe_id": recipeId,
"image_url": imageUrl,
"social_rank": socialRank,
"publisher_url": publisherUrl,
};
}
import 'package:searchscreen/data/model/pizza.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
abstract class SearchRepository{
Future<List<Recipe>> searchPizza(String query);
}
class SearchRepositoryImpl implements SearchRepository{
List<Recipe> recipes=[];
@override
Future<List<Recipe>> searchPizza(query) async{
try{
var response= await http.get( Uri.parse('https://forkify-api.herokuapp.com/api/search?q=$query' ));
if (response.statusCode == 200) {
var data = json.decode(response.body);
recipes = Pizza
.fromJson(data)
.recipes;
return recipes;
} else
{
return recipes;
}
}
catch(error){
throw (error.toString());
}
}
}
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:searchscreen/bloc/search/search_event.dart';
import 'package:searchscreen/bloc/search/search_state.dart';
import 'package:searchscreen/data/model/pizza.dart';
import 'package:searchscreen/data/repositories/search_repository.dart';
class SearchBloc extends Bloc<SearchEvent,SearchState> {
final SearchRepository repository;
SearchBloc(this.repository) : super(SearchUninitialized()) {
on<Search>(
(event, emit) async {
try {
emit(SearchLoadingState());
final List<Recipe> recipes = await repository.searchPizza(event.query);
emit(SearchLoaded(recipes: recipes));
} catch (e,stackTrace) {
emit(SearchErrorState(message: e.toString()));
}
});
}
}
bloc/search_event.dart
import 'package:equatable/equatable.dart';
abstract class SearchEvent extends Equatable{
const SearchEvent();
}
class Search extends SearchEvent{
late String query;
Search({required this.query});
@override
List<Object> get props => [];
}
bloc/search_state.dart
import 'package:equatable/equatable.dart';
import 'package:searchscreen/data/model/pizza.dart';
abstract class SearchState extends Equatable{
}
class SearchUninitialized extends SearchState{
@override
List<Object?> get props =>[];
}
class SearchLoadingState extends SearchState{
@override
List<Object?> get props => [];
}
class SearchLoaded extends SearchState{
final List<Recipe> recipes;
SearchLoaded({required this.recipes});
@override
List<Object?> get props => [];
}
class SearchErrorState extends SearchState{
final String message;
SearchErrorState({required this.message});
@override
List<Object?> get props => [];
}
main.dart
import 'package:flutter/material.dart';
import 'package:searchscreen/bloc/search/search_bloc.dart';
import 'package:searchscreen/data/repositories/search_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:searchscreen/ui/search_view.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return
RepositoryProvider(
create: (context) =>PizzaRepositoryImpl() ,
child: MultiBlocProvider(
providers: [
BlocProvider<PizzaBloc>(
create: (context) => PizzaBloc(PizzaRepositoryImpl()
)..add(FetchPizzaEvent()),lazy: false,),
BlocProvider<SearchBloc>(
create: (context) => SearchBloc(SearchRepositoryImpl()
))
],
child: MaterialApp(
title: 'Recipe Search App',
home: SearchView(),
)),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:searchscreen/bloc/search/search_bloc.dart';
import 'package:searchscreen/bloc/search/search_event.dart';
import 'package:searchscreen/bloc/search/search_state.dart';
import 'package:searchscreen/ui/list.dart';
import 'package:searchscreen/widgets/search_text_field.dart';
class SearchView extends StatefulWidget {
const SearchView({super.key});
@override
SearchViewState createState() => SearchViewState();
}
class SearchViewState extends State<SearchView> {
final TextEditingController _controller = TextEditingController();
@override
void initState() {
super.initState();
_controller.addListener(
() => context
.read<SearchBloc>()
.add(Search(query: _controller.text))
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return BlocConsumer<SearchBloc, SearchState>(
listener: (context, state) {
if (state is SearchErrorState) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(
const SnackBar(content: Text("Failed to Load")),
);
}
},
builder: (context, state) {
return Scaffold(appBar: AppBar(
title: const Text(
'Recipes'
)
),body:ListView(
shrinkWrap: true,
children: [
Card (child: SearchTextField(
key: const Key('searchPage_searchTextField'),
controller: _controller,
),),
const Divider(),
if(state is SearchLoaded)
buildHintsList(state.recipes)
],
));
},
);
}
}
|
|
|
|
Firebase is using the same storage infrastructure as google cloud .
1. Go to https://console.cloud.google.com and select the dashboard of the project that contains the firebase storage.
2. On the top right, there will be a button named "Activate Cloud Shell", click on it, a terminal will open on the bottom of the page
Click on Open Editor
Click on File, and New file.
3.Create new file with name cors.json, paste the following save.
[
{
"origin": ["*"],
"method": ["GET"],
"maxAgeSeconds": 3600
}
]
4.On your Firebase Console and select "Storage" and get your storage bucket URL (e.g gs://)
5.Then open the terminal again and paste following
gsutil cors set cors.json gs://<your-cloud-storage-bucket>
The CORS error should be gone for Storage for which we added the configuration. It wont be applied to other storage bucket
You can automate Flutter Web Deployments to Firebase Hosting using GitHub. After the configuration and setup ,all you need to do is push your local code to GitHub and view your application on web using URL .
https://firebase.google.com/docs/hosting/quickstart#install-cli
Step 2 : Initialize your project
firebase init hosting:github
Step 3 : Follow the CLI prompts, and the command will automatically take care of setting up the GitHub Action
Once the configuration is complete, there will be two new files written:
- .firebaserc : Basic config for the project. It consist of your project name on firebase;
- firebase.json: Additional config. Tells Firebase about the public folder, which files/folders to ignore
Features:
Web App link for Invoice Portal Invoice Portal App lets an user view and create an Invoice. Features: View the list of Number of Invoices b...