i'm using switch case inside future builder (below) with connection state to view certain widgets in different connection states.
My question is, how can I refactor the switch case so i can use it in other places in the app?
Thanks..
code:
FutureBuilder(
future: _fetchProductsList,
builder: (context, AsyncSnapshot<List<ProductsList>> snapshot) {
switch(snapshot.connectionState){
case ConnectionState.none:
return const Text("There is no connection!!");
case ConnectionState.active:
case ConnectionState.waiting:
return const Center(child: CircularProgressIndicator());
case ConnectionState.done:
if(snapshot.hasData){
if (snapshot.data!.isNotEmpty){
List<ProductsList>? product = snapshot.data;
return ListView.builder(
primary: false,
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: product!.length,
itemBuilder: (BuildContext context,int index){
return ProductCard(
img: product[index].image,
title: product[index].title,
price: product[index].price,
rating: product[index].rating.rate,
discount: 50,
onTap: () {}
);
}
);
}else {
// here your snapshot data is null, has no data...
return const Center(
child: Text(
"No Data Available Right Now!!"
)
);
}
}
}
// By default, show a loading spinner
return const Center(child: CircularProgressIndicator());
}
)
CodePudding user response:
You can extract the whole FutureBuilder
in a separate widget with a Generic Type to handle any type of data returned by the future
.
For example I made a widget called CustomFutureBuilder
like the following:
class CustomFutureBuilder<T> extends StatelessWidget {
final Future<T> future;
final Function(T data) successBuilder;
const CustomFutureBuilder({
Key? key,
required this.future,
required this.successBuilder,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return FutureBuilder<T>(
future: future,
builder: (context, AsyncSnapshot<T> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return const Text("There is no connection!!");
case ConnectionState.active:
case ConnectionState.waiting:
return const Center(child: CircularProgressIndicator());
case ConnectionState.done:
if (snapshot.hasData) {
successBuilder(snapshot.data!);
}
}
// By default, show a loading spinner
return const Center(child: CircularProgressIndicator());
},
);
}
}
Here I made a trick by passing a custom builder method that receives the data after all the checks, you can also pass any number of parameters you want to customize everything.
Usage Example:
CustomFutureBuilder<List>(
future: _fetchProductsList,
successBuilder: (data) {
if (data.isNotEmpty) {
List<ProductsList>? product = snapshot.data;
return ListView.builder(
primary: false,
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: product!.length,
itemBuilder: (BuildContext context, int index) {
return ProductCard(
img: product[index].image,
title: product[index].title,
price: product[index].price,
rating: product[index].rating.rate,
discount: 50,
onTap: () {});
},
);
} else {
// here your snapshot data is null, has no data...
return const Center(child: Text("No Data Available Right Now!!"));
}
},
);
CodePudding user response:
Try this demo:
future.dart file:
import 'package:flutter/material.dart';
abstract class FutureBuilderRequest extends StatelessWidget{
const FutureBuilderRequest({Key? key});
Widget hasDataWidget(dynamic data);
Widget noDataWidget();
Widget noneConnectionWidget();
Widget activeWidget();
Widget waitingWidget();
Future future();
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: future(),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return noneConnectionWidget();
case ConnectionState.active:
return noneConnectionWidget();
case ConnectionState.waiting:
return waitingWidget();
case ConnectionState.done:
if (snapshot.hasData) {
if (snapshot.data!.isNotEmpty) {
return hasDataWidget(snapshot.data);
} else {
// here your snapshot data is null, has no data...
return noDataWidget();
}
}
}
// By default, show a loading spinner
return const Center(child: CircularProgressIndicator());
},
);
}
}
main.dart :
import 'package:demo/home.dart';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: HomeScreen(),
);
}
}
home.dart
import 'package:demo/future.dart';
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(child: Body()),
);
}
}
class Body extends FutureBuilderRequest {
const Body({Key? key}) : super(key: key);
@override
Widget activeWidget() {
return Container();
}
@override
Future future() {
return Future<String>.delayed(
const Duration(seconds: 2),
() => 'Data Loaded',
);
}
@override
Widget hasDataWidget(data) {
return Column(
children: [
const Icon(
Icons.check_circle_outline,
color: Colors.green,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Result: ${data}'),
)
],
);
}
@override
Widget noDataWidget() {
return Container();
}
@override
Widget noneConnectionWidget() {
return Container();
}
@override
Widget waitingWidget() {
return const Center(
child: CircularProgressIndicator(),
);
}
}