Here we will see how to create a custom tab bar and load network data using RepositoryProvider and how tabs loads data on button click.
Our tab bar would be responsive to click which means we will maintain state of the tabs. Certain tab will load certain data from the network.
Network data loader functions would be defined in Repository class. We will create our custom repository provider and http get requests would be there to load data.
To follow this tutorial you need to install below 3 packages.
- flutter_bloc
- equatable
- http
Here I will cover
- maintain state using flutter_bloc
- toggle boolean values and update ui
- access the value from the ui
- use RepositoryProvider inject resources and BlocProvider
- inject controller inside bloc provider
- use BlocBuilder to read the value
See the starter code below. The below code should be put in your HomePage class.
Widget _headTabs() {
return Center(
child: Container(
width: 320,
height: 48,
margin: EdgeInsets.only(bottom: 0),
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.5),
borderRadius: BorderRadius.all(Radius.circular(5)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
child: Container(
width: 150,
height: 40,
margin: EdgeInsets.only(bottom: 0),
padding: EdgeInsets.all(0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
const BorderRadius.all(Radius.circular(5)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 2,
blurRadius: 3,
offset: Offset(
0, 2), // changes position of shadow
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
child: Text(
"Chat",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.normal,
fontSize: 14,
),
),
),
],
),
),
onTap: () {
}),
],
)));
}
We have the above code used for creating 2 buttons. Two buttons corresponds to different button clicks and would do different things.
See the app states class.
import 'package:equatable/equatable.dart';
class AppStates extends Equatable {
const AppStates({
this.tabStatus=true,
});
final bool tabStatus;
AppStates changeTab() {
return AppStates(
tabStatus: !tabStatus ,
);
}
@override
List<Object> get props => [tabStatus];
}
Here tabStatus is the variable that would be stateful and change it’s values on the button click. We will toggle this variable value.
changeTab() would change the value. This method would be called from UI. Let’s take a look at the event class.
abstract class AppEvents extends Equatable{
const AppEvents();
@override
List<Object> get props => [];
}
class ChangeTab extends AppEvents {
const ChangeTab();
@override
List<Object> get props => [];
}
Now since we have both states and events, we can combine them together and create our blocs. See the bloc class
class AppBlocs extends Bloc<AppEvents, AppStates> {
final DataRepository dataRepo;
AppBlocs({required this.dataRepo}) : super(const AppStates()) {
on<ChangeTab>(_changeTabs);
}
Future<void> _changeTabs(
ChangeTab event,
Emitter<AppStates> emit,
) async {
emit(state.changeTab());
if(state.tabStatus==true){
dataRepo.loadPopularProducts();
}else{
dataRepo.loadRecommendedProducts();
}
}
}
In our bloc class we have repository methods. We access them using dataRepo which is sent from RepositoryProvider in the main.dart class.
Look at our repository class that gets data from the network.
class DataRepository{
var url1 = Uri.parse("http://mvs.bslmeiyu.com/api/v1/products/popular");
var url2 = Uri.parse("http://mvs.bslmeiyu.com/api/v1/products/recommended");
void loadPopularProducts() async{
http.Response response = await http.get(
url1
);
print("popular products ${response.body.toString()}");
}
void loadRecommendedProducts() async{
http.Response response = await http.get(
url2
);
print("recommended products ${response.body.toString()}");
}
}
finally look at our main.dart file
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return RepositoryProvider(
create: (context) => DataRepository()..loadPopularProducts(),
child: BlocProvider(
create: (context)=>AppBlocs(
dataRepo:RepositoryProvider.of<DataRepository>(context)
),
child: MaterialApp(
home: HomePage(),
),
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
Widget _headTabs(BuildContext context) {
return Center(
child: Container(
width: 320,
height: 48,
margin: EdgeInsets.only(bottom: 0),
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.5),
borderRadius: BorderRadius.all(Radius.circular(5)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
child: Container(
width: 150,
height: 40,
margin: EdgeInsets.only(bottom: 0),
padding: EdgeInsets.all(0),
decoration: BlocProvider.of<AppBlocs>(context)
.state
.tabStatus
? BoxDecoration(
color: Colors.white,
borderRadius:
const BorderRadius.all(Radius.circular(5)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 2,
blurRadius: 3,
offset: Offset(
0, 2), // changes position of shadow
),
],
)
: BoxDecoration(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
child: Text(
"popular",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.normal,
fontSize: 14,
),
),
),
],
),
),
onTap: () {
BlocProvider.of<AppBlocs>(context).add(ChangeTab());
}),
GestureDetector(
child: Container(
width: 150,
height: 40,
margin: EdgeInsets.only(bottom: 0),
padding: EdgeInsets.all(0),
decoration: BlocProvider.of<AppBlocs>(context)
.state
.tabStatus ==
false
? BoxDecoration(
color: Colors.white,
borderRadius:
const BorderRadius.all(Radius.circular(5)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 2,
blurRadius: 3,
offset: Offset(
0, 2), // changes position of shadow
),
],
)
: BoxDecoration(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
child: Text(
"recommend",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.normal,
fontSize: 14,
),
),
),
],
),
),
onTap: () {
BlocProvider.of<AppBlocs>(context).add(ChangeTab());
}),
],
)));
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BlocBuilder<AppBlocs, AppStates>(builder: (context, state) {
return _headTabs(context);
})
],
))),
);
}
}
Conclusion
So here you learned how to toggle boolean and maintain state using BLoC, then how to show the changed value in the UI. We also learned how to use RepositoryProvider and auto load data using double dots operator. You also learned how to load network data using http package based on toggle value.
BLoC clean architecture with TDD