How can we integrate Flutter with .Net core web API | Part-2: Building the Flutter App.
The application simply do the following activities.
- Displaying the Services on the main screen
- Showing the detail of every service listed on the main screen
- It will have edit option for every service
- It will have a delete option for every service.
This is the main page of the application that will display the services lists.

and this is the service add and update page.

and this one is the service detail page on this page we can delete and edit the single service.

so here we will follow some steps to buildthis application.
Pre requests
1.Installing Android studio
2.Installing Flutter and dart
Step 1: Creating a new Flutter Project from an android studio or visual studio code.
From the android studio go to File and click on the New then select New Flutter Project option.

Then you will get the following page.
Click on the Flutter Application and click on the Next button

then here give the application a new name. and click on the Next button.


Then click on the Finish button at the bottom.
here we get a default code.

First, we should prepare a debugging emulator or connect our phone. and it will come at the top.

after that click on the green run button at the top. you will get this page on the emulator.

leave this code, for now, we will update it later.
now create a new directory called Service inside the lib package and inside this directory create the following directories.

Let us see step by step the code insides these directories.
What is Bloc State Management?
Bloc Overview
Overall, Bloc attempts to make state changes predictable by regulating when a state change can occur and enforcing a single way to change state throughout an entire application.
Below are common developer needs that Bloc tries to address
- Working as efficiently as possible and reuse components both within your application and across other applications.
- Letting many developers to seamlessly work within a single code base
- following the same patterns and conventions Developing fast and reactive apps
- knowing what state your application is in at any point in time
- Easily testing every case to make sure our app is responding
appropriately - Recording every single user interaction in your application so that you can make data-driven decisions
Installing and Importing Bloc package
Installation
Add the flutter_bloc package to your pubspec.yaml as a dependency
dependencies:
flutter_bloc: ^6.1.1
Import
import 'package:flutter_bloc/flutter_bloc.dart';
Bloc Architecture

Bloc Architecture: Data Layer
The data layer’s responsibility is to retrieve/manipulate data from one or more sources The data layer can be split into two parts
Repository
Data Provider
This layer is the lowest level of the application and interacts with databases, network requests, and other asynchronous data sources
Bloc Architecture: Data Layer -> Data Provider
The data provider’s responsibility is to provide raw data. It should be generic and versatile and it will usually expose simple APIs to perform CRUD operations. We might have a createData, readData, updateData, and deleteData method as part of our data layer.
Inside the DataProvider directory create the following two dart files.

Service_data.dart
then inside the Service_data.dart file first add the necessary imports.
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter_group_project/Service/Model/Service.dart';
import 'package:meta/meta.dart';
import 'package:http/http.dart' as HTTP;
create a class called ServiceDataProvider
class ServiceDataProvider {
}
then inside this class first define the Url for which it will fetch the data from the back-end API
final _baseUrl = 'http://192.168.42.232:5000/api';
Then define the httpClient
final http.Client httpClient;
next, add the constructor for the class.and inside the constructor specify the necessary parameter .
ServiceDataProvider({@required this.httpClient}) : assert(httpClient != null);
after this let’s we add the code for the CRUD methods.
createService
this method will communicate with the back-end in order to create the data at the back-end database.
First we have to define the method signature for the createService method as follows.
Future<Service> createService(Service service) async {
}
then let us define the response
final response = await httpClient.post(
)
Inside this httpClient.post method we should put some parameters which are essesntial for the http call.
URL
The first one is the URL this is the destination where the method invocation will go.
Uri.http('http://192.168.42.232:5000', '/api/services'),
Header
then we should give the header information. on this information we have to tell the back-end the
type of files we are passing.
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
Body
then the last on is the body this will be the data that will be created at the server.
body: jsonEncode(<String, dynamic>{
"serviceName": service.ServiceName,
"description": service.Description,
"category": service.Category,
"initialPrice": service.InitialPrice,
"intermediatePrice":service.IntermediatePrice,
"advancedPrice":service.AdvancedPrice,
}),
The whole response code will look like the following.
final response = await httpClient.post(
Uri.http('http://192.168.42.232:5000', '/api/services'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, dynamic>{
"serviceName": service.ServiceName,
"description": service.Description,
"category": service.Category,
"initialPrice": service.InitialPrice,
"intermediatePrice":service.IntermediatePrice,
"advancedPrice":service.AdvancedPrice,
}),
);
after the response successfully define, based on the response that come from the server we will update the state
if (response.statusCode == 200) {
return Service.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to create course.');
}
The whole createService method code looks like this.
Future<Service> createService(Service service) async {
final response = await httpClient.post(
Uri.http('http://192.168.42.232:5000', '/api/services'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, dynamic>{
"serviceName": service.ServiceName,
"description": service.Description,
"category": service.Category,
"initialPrice": service.InitialPrice,
"intermediatePrice":service.IntermediatePrice,
"advancedPrice":service.AdvancedPrice,
}),
);
if (response.statusCode == 200) {
return Service.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to create course.');
}
}
getService
Future<List<Service>> getServices() async {
try{
final response = await http.get('${_baseUrl}/services');
if (response.statusCode == 200) {
final services = jsonDecode(response.body) as List;
return services.map((service) => Service.fromJson(service)).toList();
} else {
throw Exception('Failed to load courses');
}
}catch(e){
print("Exception throuwn $e");
}
}
deleteService
Future<void> deleteService(int id) async {
final http.Response response = await http.delete(
'http://192.168.43.163:5000/Service/$id',
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
);
if (response.statusCode != 200) {
throw Exception('Failed to delete course.');
}
}
updateService
Future<void> updateService(Service service) async {
final http.Response response = await httpClient.put(
'http://192.168.43.163:5000/Service/',
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, dynamic>{
"serviceId":service.id,
"serviceName": service.ServiceName,
"description": service.Description,
"category": service.Category,
"initialPrice": service.InitialPrice,
"intermediatePrice":service.IntermediatePrice,
"advancedPrice":service.AdvancedPrice,
}),
);
if (response.statusCode != 200) {
throw Exception('Failed to update course.');
}
}
The overall service_data.dart code will be like the following.
“`dart
import ‘dart:convert’;
import ‘package:flutter/cupertino.dart’;
import ‘package:flutter_group_project/Service/Model/Service.dart’;
import ‘package:meta/meta.dart’;
import ‘package:http/http.dart’ as http;
class ServiceDataProvider {
final _baseUrl = ‘http://192.168.42.232:5000/api’;
final http.Client httpClient;
ServiceDataProvider({@required this.httpClient}) : assert(httpClient != null);
Future createService(Service service) async {
final response = await httpClient.post(
Uri.http('http://192.168.42.232:5000', '/api/services'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, dynamic>{
"serviceName": service.ServiceName,
"description": service.Description,
"category": service.Category,
"initialPrice": service.InitialPrice,
"intermediatePrice":service.IntermediatePrice,
"advancedPrice":service.AdvancedPrice,
}),
);
if (response.statusCode == 200) {
return Service.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to create course.');
}
}
Future> getServices() async {
try{
final response = await http.get('${_baseUrl}/services');
if (response.statusCode == 200) {
final services = jsonDecode(response.body) as List;
return services.map((service) => Service.fromJson(service)).toList();
} else {
throw Exception('Failed to load courses');
}
}catch(e){
print(“Exception throuwn $e”);
}
}
Future deleteService(int id) async {
final http.Response response = await http.delete(
‘http://192.168.43.163:5000/Service/$id’,
headers: {
‘Content-Type’: ‘application/json; charset=UTF-8’,
},
);
if (response.statusCode != 200) {
throw Exception('Failed to delete course.');
}
}
Future updateService(Service service) async {
final http.Response response = await httpClient.put(
‘http://192.168.43.163:5000/Service/’,
headers: {
‘Content-Type’: ‘application/json; charset=UTF-8’,
},
body: jsonEncode({
“serviceId”:service.id,
“serviceName”: service.ServiceName,
“description”: service.Description,
“category”: service.Category,
“initialPrice”: service.InitialPrice,
“intermediatePrice”:service.IntermediatePrice,
“advancedPrice”:service.AdvancedPrice,
}),
);
if (response.statusCode != 200) {
throw Exception('Failed to update course.');
}
}
}
**data_provider.dart**
this will export the **Service_data** add the following line of code inside.
dart
export ‘Service_data.dart’;
**Bloc Architecture: Data Layer -> Repository**
The repository layer is a wrapper around one or more data providers with which the Bloc Layer communicates. It can interact with multiple data providers and perform transformations on the data before handing the result to the business logic layer.
Inside the **Repository** directory let us create the following two dart files.

Then inside the **Service_repository.dart** file we are going to create a function for all of the data provider **CRUD** method.
this method will serve as a mediator between the **business logic layer** and the **data access layer**.
dart
import ‘package:flutter_group_project/Service/Model/Service.dart’;
import ‘package:meta/meta.dart’;
import ‘../Service.dart’;
class ServiceRepository {
final ServiceDataProvider dataProvider;
ServiceRepository({@required this.dataProvider})
: assert(dataProvider != null);
Future createService(Service service) async {
return await dataProvider.createService(service);
}
Future> getServices() async {
print(“This is the getService method”);
return await dataProvider.getServices();
}
Future updateService(Service service) async {
await dataProvider.updateService(service);
}
Future deleteService(int id) async {
await dataProvider.deleteService(id);
}
}
The **repository.dart** file does nothing but exporting the *Service_repository.dart*.
dart
export ‘Service_repository.dart’;
**Bloc Architecture: Business Logic Layer**
The business logic layer's responsibility is to respond to input from the presentation layer with new states. This layer can depend on one or more repositories to retrieve data needed to
build up the application state. Think of the business logic layer as the **bridge** between the user interface (presentation layer) and the data layer.
The business logic layer is notified of events/actions from the presentation layer and then communicates with the repository in order to build a new state for the presentation layer to consume.
From the **Bloc** directory create the following dart files.

The bloc state management has three different elements.
* State
* Event
* bloc
**State**
The State will represent the state for the application. this will be the defined state that the app will possess.
open **Service_state.dart** file and add the following code inside.
dart
import ‘package:equatable/equatable.dart’;
import ‘package:flutter_group_project/Service/Service.dart’;
class ServiceState extends Equatable {
const ServiceState();
@override
List get props => [];
}
class ServiceLoading extends ServiceState {}
class ServicesLoadSuccess extends ServiceState{
final List services;
ServicesLoadSuccess([this.services = const []]);
@override
List get props => [services];
}
class ServiceOperationFailure extends ServiceState {}
As you can see from the code we have three different states. **ServiceLoading**,**ServicesLoadSuccess**,**ServiceOperationFailer**
**Event**
The event means the operation that will take place in the application. this will be the same as the **CRUD** operation that we have registered in the data provider.
For this open the **Service_event.dart** file and add the following code inside.
dart
import ‘package:equatable/equatable.dart’;
import ‘package:flutter_group_project/Service/Model/Service.dart’;
import ”;
abstract class ServiceEvent extends Equatable {
const ServiceEvent();
}
class ServiceLoad extends ServiceEvent {
const ServiceLoad();
@override
List get props => [];
}
class ServiceCreate extends ServiceEvent {
final Service service;
const ServiceCreate(this.service);
@override
List get props => [service];
@override
String toString() => ‘Service Created {service: $service}’;
}
class ServiceUpdate extends ServiceEvent {
final Service service;
const ServiceUpdate(this.service);
@override
List get props => [service];
@override
String toString() => ‘Service Updated {service: $service}’;
}
class ServiceDelete extends ServiceEvent {
final Service service;
const ServiceDelete(this.service);
@override
List get props => [service];
@override
toString() => ‘Service Deleted {service: $service}’;
}
here we have 4 different Events These are listed below with their correspondence data provider method.
* ServiceLoad -> getServices()
* ServiceCreate -> createService()
* ServiceUpdate -> updateService()
* ServiceDelete -> deleteService()
**bloc**
This dart file will map every event invoked by the presentation layer to the state. then it will send the updated state back to the presentation layer.
open the **Service_bloc.dart** file and add the following code.
dart
import ‘package:flutter/cupertino.dart’;
import ‘package:flutter_bloc/flutter_bloc.dart’;
import ‘package:flutter_group_project/Service/Bloc/bloc.dart’;
import ‘package:flutter_group_project/Service/Repository/Service_repository.dart’;
class ServiceBloc extends Bloc {
final ServiceRepository serviceRepository;
ServiceBloc({@required this.serviceRepository})
: assert(serviceRepository != null),
super(ServiceLoading());
@override
Stream mapEventToState(ServiceEvent event) async* {
if (event is ServiceLoad) {
print(“Service load method”);
yield ServiceLoading();
try {
final services = await serviceRepository.getServices();
print("This is the service $services");
yield ServicesLoadSuccess(services);
} catch (_) {
yield ServiceOperationFailure();
}
}
if (event is ServiceCreate) {
try {
await serviceRepository.createService(event.service);
final services = await serviceRepository.getServices();
yield ServicesLoadSuccess(services);
} catch (_) {
yield ServiceOperationFailure();
}
}
if (event is ServiceUpdate) {
try {
await serviceRepository.updateService(event.service);
final services = await serviceRepository.getServices();
yield ServicesLoadSuccess(services);
} catch (_) {
yield ServiceOperationFailure();
}
}
if (event is ServiceDelete) {
try {
await serviceRepository.deleteService(event.service.id);
final services = await serviceRepository.getServices();
yield ServicesLoadSuccess(services);
} catch (e) {
print("Error de,ete=$e");
yield ServiceOperationFailure();
}
}
}
}
and finally, there is one file that remained called **bloc.dart** this file will export all three files once. so in other than importing all three files separately importing only this file will make our code simpler and minimize the line of code.
dart
export ‘Service_bloc.dart’;
export ‘Service_state.dart’;
export ‘Service_event.dart’;
If you came from the react js web development background, this type of state management is exactly the same as the **React Redux** state management
* Bloc -> Reducer
* State -> State
* Event -> Action
**Bloc Architecture: Presentation Layer**
The presentation layer's responsibility is to figure out how to render itself based on one or more bloc states In addition, it should handle user input and application lifecycle events Most applications flows will start with an AppStart event which triggers the application to fetch some data to present to the user
In this scenario, the presentation layer would add an AppStart event.
We are going to build the **UI** on this step.
In the **Screen** directory, create the following dart files.

The main page of the app looks like the following.

First, there will be an import from the above. we are gonna import all the necessary packages we need to integrate the **UI** with the business logic and for layouts.
dart
import ‘package:flutter/material.dart’;
import ‘package:flutter_bloc/flutter_bloc.dart’;
import ‘package:flutter_group_project/Service/Bloc/bloc.dart’;
import ‘package:flutter_group_project/Service/Screen/ServiceCreateUpdateScreen.dart’;
import ‘package:flutter_group_project/Service/Screen/screens.dart’;
import ‘dart:math’;
Create a class called ServiceMainScreen and define the class **routeName** as Static.
dart
class ServiceMainScreen extends StatelessWidget {
static const routeName=’/category_screen’;
}
The first method we are going to use is a **buildSectionTitle** and this method is used to build a custom text widget for the title.
dart
Widget buildSectionTitle(BuildContext context, String text){
return Container(
margin: EdgeInsets.symmetric(
vertical: 10,
),
child: Text('${text}',
style: Theme.of(context).textTheme.headline6,),
);
}
and next, we have here a **buildContainer** method that used to build a container, we will use this method later.
dart
Widget buildContainer(BuildContext context , Widget child){
return Container(
height: MediaQuery.of(context).size.height * 0.5 ,
width:MediaQuery.of(context).size.width * 0.90 ,
decoration: BoxDecoration(
color:Colors.white,
border:Border.all(
color:Colors.grey
),
borderRadius: BorderRadius.circular(10),
),
child:child,
);
}
**Build Method**
This is the defualt method that will be overriden. this method will start rendering the application so that the app start the building process from this method.
It have a BuildContext parameter.
dart
@override
Widget build(BuildContext context) {
Random random = new Random();
int randomNumber = random.nextInt(6);
// return statment here
}
we will use the random number later.
the build methos should return a widget.we are going to return a **Scaffold** from the build method.
dart
return Scaffold(
//
)
the scaffold widget have might have different named parameters depending on the app we are building for this application we are
going to use the following named parameters inside the **Scaffold** widget.
* appBar
* body
* floatingActionbutton
* floatingActionButtonLocation
* bottomNavigationBar
**appBar**
This is the top navigation bar.

dart
appBar: AppBar(title: Text(‘Category Name’), actions: [
PopupMenuButton(
icon: Icon(Icons.refresh),
itemBuilder: (_) => [
PopupMenuItem(
child: Text(‘Refresh’),
value: () {},
),
PopupMenuItem(
child: Text(‘Show All’),
value: null,
),
]),
]),
**body**
This is the main portion of the application, it will cover the place other than the **bottomNavigationBar** and the **floatingActionButton**

Because the whole page will scroll we have to held using a **singleChildScrolView** at the top.
dart
body: SingleChildScrollView(
child: Column(
children: [
// Childrens will come here
]
)
Then inside the children widget we have the following childrent respectively.
* Image Section
* Text Section
* List View
**Image Section**

dart
Container(
height: MediaQuery.of(context).size.width * 0.5,
width: double.infinity,
child: Image.asset(“Assets/assets/wood.jpg”),
),
**Text Section**

dart
buildSectionTitle(context, ‘Services’),
If you remember in the above we have implemented this **buildSectionTitle** method, so that we are using it here to build a custome text Wdiget.
**List View**

This section is the place where the **Services** that came from the back-end will be displayed, so this widget is a replaceble widget depending on the new state that came.
we are going to use our **Bloc** concept here.
**Core Concepts of Bloc: BlocBuilder**
BlocBuilder is a Flutter widget which requires a Bloc and a builder function BlocBuilder handles building the widget in response to new states The builder function will potentially be called many times and should be a pure function that returns a widget in response to the state.
Next to the **Service** title section put the following **blocBuilder** method signature.
dart
BlocBuilder(builder: (_, state) {
// remainging code here
}
Then inside this method let us add our business logic, this page only loads data from the **businessLogicLayer** so the possible states will be the following.
* ServiceOperationFailure
* ServicesLoadSuccess
so let us do a confition for these states.
**ServiceOperationFailure**
we do not have much to do here only return a Text.
dart
if (state is ServiceOperationFailure) {
return Text(‘Could not do Service operation’);
}
**ServicesLoadSuccess**
here our on this condition our list view will be build depending on the States that come from **businessLogicLayer**.
dart
if (state is ServicesLoadSuccess) {
final services = state.services;
return buildContainer(
context,
ListView.builder(
itemCount: services.length,
itemBuilder: (ctx, index) => Column(children: [
Container(
decoration: BoxDecoration(
color: Colors.white38,
borderRadius: BorderRadius.circular(20)),
height:
MediaQuery.of(context).size.height * 0.15,
margin: EdgeInsets.all(4),
padding: EdgeInsets.all(4),
child: Card(
elevation: 5,
margin: EdgeInsets.symmetric(
vertical: 8, horizontal: 5),
child: Center(
child: ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage(
‘Assets/assets/fixit.png’),
),
title: Text(
services[index].ServiceName != null
? services[index].ServiceName
: “place holder”,
style: Theme.of(context)
.textTheme
.headline6,
),
subtitle: Text(
services[index].Description != null
? services[index].Description
: “Place holder”,
),
trailing: MediaQuery.of(context)
.size
.width >
450
? FlatButton.icon(
textColor:
Theme.of(context).errorColor,
icon: Icon(
Icons.delete_forever,
color: Theme.of(context)
.errorColor,
),
)
: IconButton(
icon: Icon(
Icons.star_border,
color: Theme.of(context)
.errorColor,
),
),
onTap: () => Navigator.of(context)
.pushNamed(ServiceDetail.routeName,
arguments: services[index]),
),
),
),
)
])));
}
then on the defualt position let us return a progress indicator.
dart
return CircularProgressIndicator();
The overall **BlocBuilder** code will looks like the following.
dart
BlocBuilder(builder: (_, state) {
if (state is ServiceOperationFailure) {
return Text('Could not do Service operation');
}
if (state is ServicesLoadSuccess) {
final services = state.services;
return buildContainer(
context,
ListView.builder(
itemCount: services.length,
itemBuilder: (ctx, index) => Column(children: [
Container(
decoration: BoxDecoration(
color: Colors.white38,
borderRadius: BorderRadius.circular(20)),
height:
MediaQuery.of(context).size.height * 0.15,
margin: EdgeInsets.all(4),
padding: EdgeInsets.all(4),
child: Card(
elevation: 5,
margin: EdgeInsets.symmetric(
vertical: 8, horizontal: 5),
child: Center(
child: ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage(
'Assets/assets/fixit.png'),
),
title: Text(
services[index].ServiceName != null
? services[index].ServiceName
: "place holder",
style: Theme.of(context)
.textTheme
.headline6,
),
subtitle: Text(
services[index].Description != null
? services[index].Description
: "Place holder",
),
trailing: MediaQuery.of(context)
.size
.width >
450
? FlatButton.icon(
textColor:
Theme.of(context).errorColor,
icon: Icon(
Icons.delete_forever,
color: Theme.of(context)
.errorColor,
),
)
: IconButton(
icon: Icon(
Icons.star_border,
color: Theme.of(context)
.errorColor,
),
),
onTap: () => Navigator.of(context)
.pushNamed(ServiceDetail.routeName,
arguments: services[index]),
),
),
),
)
])));
}
return CircularProgressIndicator();
}),
So by this we have finalized the **body**.
**floatingActionButton**

This action button will be use to add additional services.
dart
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.of(context).pushNamed(
AddUpdateService.routeName,
arguments: ServiceArgument(edit: false),
),
child: Icon(Icons.add),
),
To make the **floatingActionButton** have a dom shaped and stayed at the center of the bottom navigation bar let us give it the follwoing property.
dart
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
**bottomNavigationBar**
This part have no any functionality for this application but incase of a serious project we can give it some actoin to perform. here it is just a default for a decorating the layout.

dart
bottomNavigationBar: BottomAppBar(
color: Theme.of(context).primaryColor,
shape: CircularNotchedRectangle(),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
IconButton(
icon: Icon(Icons.search),
color: Colors.white,
onPressed: () {
print(“search icon button have been clicked”);
},
),
IconButton(
icon: Icon(Icons.note),
color: Colors.white,
onPressed: () {
print(“the note icon button have been clicked”);
},
)
]),
)
The whole **ServiceMainScreen.dart** file will be as the follwoing.
dart
import ‘package:flutter/cupertino.dart’;
import ‘package:flutter/material.dart’;
import ‘package:flutter_bloc/flutter_bloc.dart’;
import ‘package:flutter_group_project/Service/Bloc/bloc.dart’;
import ‘package:flutter_group_project/Service/Screen/ServiceCreateUpdateScreen.dart’;
import ‘package:flutter_group_project/Service/Screen/screens.dart’;
import ‘dart:math’;
class ServiceMainScreen extends StatelessWidget {
static const routeName = ‘/category_screen’;
Widget buildSectionTitle(BuildContext context, String text) {
return Container(
margin: EdgeInsets.symmetric(
vertical: 10,
),
child: Text(
‘${text}’,
style: Theme.of(context).textTheme.headline6,
),
);
}
Widget buildContainer(BuildContext context, Widget child) {
return Container(
height: MediaQuery.of(context).size.height * 0.5,
width: MediaQuery.of(context).size.width * 0.90,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(10),
),
child: child,
);
}
@override
Widget build(BuildContext context) {
Random random = new Random();
int randomNumber = random.nextInt(6);
print(randomNumber);
return Scaffold(
appBar: AppBar(title: Text('Category Name'), actions: [
PopupMenuButton(
icon: Icon(Icons.refresh),
itemBuilder: (_) => [
PopupMenuItem(
child: Text('Refresh'),
value: () {},
),
PopupMenuItem(
child: Text('Show All'),
value: null,
),
]),
]),
body: SingleChildScrollView(
child: Column(children: [
Container(
height: MediaQuery.of(context).size.width * 0.5,
width: double.infinity,
child: Image.asset("Assets/assets/wood.jpg"),
),
// ingredient
buildSectionTitle(context, ‘Services’),
BlocBuilder<ServiceBloc, ServiceState>(builder: (_, state) {
if (state is ServiceOperationFailure) {
return Text('Could not do Service operation');
}
if (state is ServicesLoadSuccess) {
final services = state.services;
return buildContainer(
context,
ListView.builder(
itemCount: services.length,
itemBuilder: (ctx, index) => Column(children: [
Container(
decoration: BoxDecoration(
color: Colors.white38,
borderRadius: BorderRadius.circular(20)),
height:
MediaQuery.of(context).size.height * 0.15,
margin: EdgeInsets.all(4),
padding: EdgeInsets.all(4),
child: Card(
elevation: 5,
margin: EdgeInsets.symmetric(
vertical: 8, horizontal: 5),
child: Center(
child: ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage(
'Assets/assets/fixit.png'),
),
title: Text(
services[index].ServiceName != null
? services[index].ServiceName
: "place holder",
style: Theme.of(context)
.textTheme
.headline6,
),
subtitle: Text(
services[index].Description != null
? services[index].Description
: "Place holder",
),
trailing: MediaQuery.of(context)
.size
.width >
450
? FlatButton.icon(
textColor:
Theme.of(context).errorColor,
icon: Icon(
Icons.delete_forever,
color: Theme.of(context)
.errorColor,
),
)
: IconButton(
icon: Icon(
Icons.star_border,
color: Theme.of(context)
.errorColor,
),
),
onTap: () => Navigator.of(context)
.pushNamed(ServiceDetail.routeName,
arguments: services[index]),
),
),
),
)
])));
}
return CircularProgressIndicator();
}),
]),
),
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.of(context).pushNamed(
AddUpdateService.routeName,
arguments: ServiceArgument(edit: false),
),
child: Icon(Icons.add),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: BottomAppBar(
color: Theme.of(context).primaryColor,
shape: CircularNotchedRectangle(),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
IconButton(
icon: Icon(Icons.search),
color: Colors.white,
onPressed: () {
print("search icon button have been clicked");
},
),
IconButton(
icon: Icon(Icons.note),
color: Colors.white,
onPressed: () {
print("the note icon button have been clicked");
},
)
]),
));
}
}
“`