Reputation: 225
I am trying to display one SingleChildListView containing THREE seperate FutureBuilder with ListView.sperator. I am using Provider for fetching data from the SQFLite database.
Thi is my code:
class SelectCategoryPage extends StatefulWidget {
const SelectCategoryPage({Key? key}) : super(key: key);
@override
State<SelectCategoryPage> createState() => _SelectCategoryPageState();
}
class _SelectCategoryPageState extends State<SelectCategoryPage> {
//
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kBackgroundColor,
appBar: _appBar(),
body: _body(context),
bottomNavigationBar: _bottomNavigationBar(),
);
}
AppBar _appBar() {
return AppBar(
elevation: 0,
backgroundColor: Colors.white,
title: Text(
'Select Category',
style: appBarHeaderTStyle,
),
iconTheme: const IconThemeData(color: Colors.black),
actions: [
Consumer<SelectCategoryViewModel>(
builder: (context, provider, child) {
return TextButton(
child: Text(
provider.getEditOptionData ? 'Save' : 'Edit',
style: textButtonTStyle,
),
onPressed: () {
provider.toggleEditButton();
},
);
},
),
],
);
}
Widget _body(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 20, left: 24, right: 24),
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
controller: null,
physics: const BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 12),
Text(
'Expense',
style: selectCategoryHeaderTStyle,
),
const SizedBox(height: 16),
//
_expenseCategory(context),
//
const SizedBox(height: 24),
Text(
'Income',
style: selectCategoryHeaderTStyle,
),
const SizedBox(height: 16),
//
_incomeCategory(context),
//
const SizedBox(height: 24),
Text(
'Other',
style: selectCategoryHeaderTStyle,
),
const SizedBox(height: 16),
//
_otherCategory(context),
//
const SizedBox(height: 20),
],
),
),
),
],
),
);
}
FutureBuilder<void> _expenseCategory(BuildContext context) {
return FutureBuilder(
future: Provider.of<SelectCategoryViewModel>(
context,
listen: false,
).selectCategoryByExpenseType(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
return Consumer<SelectCategoryViewModel>(
child: const Center(child: Text('No Data')),
builder: (context, expenseProvider, child) => expenseProvider
.data.isEmpty
? child!
: ListView.separated(
shrinkWrap: true,
itemCount: expenseProvider.data.length,
physics: const BouncingScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const SizedBox(height: 16);
},
itemBuilder: (BuildContext context, int index) {
return SelectCategoryCard(
id: expenseProvider.data[index].id,
coloredIcon: expenseProvider.data[index].categoryIcon,
title: expenseProvider.data[index].categoryName,
isOptionDotsVisitable:
expenseProvider.getEditOptionData,
);
},
),
);
default:
return Container();
}
},
);
}
FutureBuilder<void> _incomeCategory(BuildContext context) {
return FutureBuilder(
future: Provider.of<SelectCategoryViewModel>(
context,
listen: false,
).selectCategoryByIncomeType(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
return Consumer<SelectCategoryViewModel>(
child: const Center(child: Text('No Data')),
builder: (context, incomeProvider, child) => incomeProvider
.data.isEmpty
? child!
: ListView.separated(
shrinkWrap: true,
itemCount: incomeProvider.data.length,
physics: const BouncingScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const SizedBox(height: 16);
},
itemBuilder: (BuildContext context, int index) {
return SelectCategoryCard(
id: incomeProvider.data[index].id,
coloredIcon: incomeProvider.data[index].categoryIcon,
title: incomeProvider.data[index].categoryName,
isOptionDotsVisitable:
incomeProvider.getEditOptionData,
);
},
),
);
default:
return Container();
}
},
);
}
FutureBuilder<void> _otherCategory(BuildContext context) {
return FutureBuilder(
future: Provider.of<SelectCategoryViewModel>(
context,
listen: false,
).selectCategoryByOtherType(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
return Consumer<SelectCategoryViewModel>(
child: const Center(child: Text('No Data')),
builder: (context, otherProvider, child) => otherProvider
.data.isEmpty
? child!
: ListView.separated(
shrinkWrap: true,
itemCount: otherProvider.data.length,
physics: const BouncingScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const SizedBox(height: 16);
},
itemBuilder: (BuildContext context, int index) {
return SelectCategoryCard(
id: otherProvider.data[index].id,
coloredIcon: otherProvider.data[index].categoryIcon,
title: otherProvider.data[index].categoryName,
isOptionDotsVisitable:
otherProvider.getEditOptionData,
);
},
),
);
default:
return Container();
}
},
);
}
Widget _bottomNavigationBar() {
return CustomBottomAppBar(
buttonText: '+ Add New Category',
onTapEvent: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CreateNewCategoryPage(),
),
);
},
);
}
}
Only the last FutureBuilder is working properly, and other builders are showing the exact output as the last (3rd) FutureBuilder.
Upvotes: 0
Views: 35
Reputation: 667
Went through the same thing once. You need to have only one future builder and a Future that gets all the data and assigns it. Something like this:
class _DeviceInformationScreenState extends State<DeviceInformationScreen> {
late StaticConfiguration staticConfiguration;
late PackageInfo packageInfo;
late DeviceData deviceData;
late QuarkusHealthClient quarkusHealth;
LisaInfo lisaInfo = LisaInfo();
//TODO: add translations?
Future<void> _getAppInfo() async {
packageInfo = await PackageInfo.fromPlatform();
staticConfiguration = await config.readStaticConfiguration();
deviceData = await DeviceData.fromDevice();
quarkusHealth = await QuarkusHealthClient.checkHealth(staticConfiguration.grpcServerHost);
lisaInfo = await lisaInfo.getLisaInfo();
}
@override
Widget build(BuildContext context) {
return buildToolsScaffold(context,
body: FutureBuilder(
future: _getAppInfo(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 50.0),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
Text(
T.screens.healthInfo.configData,
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(
height: 20,
),
_buildConfigDataWidgets()
],
),
And _buildConfigDataWidgets()
looks like this:
Widget _buildConfigDataWidgets() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(T.screens.healthInfo.data.configFile +
(staticConfiguration.configFileExists
? T.screens.healthInfo.data.exists
: T.screens.healthInfo.data.doesNotExist)),
const SizedBox(
height: 10,
),
Text(T.screens.healthInfo.data.grpcHost + ': ' + staticConfiguration.grpcServerHost),
const SizedBox(
height: 10,
),
Text(T.screens.healthInfo.data.grpcPort + ': ' + staticConfiguration.grpcServerPort.toString()),
const SizedBox(
height: 10,
),
if (staticConfiguration.locale != null) ...[
Text(T.screens.healthInfo.data.activeLanguageCode + ': ' + staticConfiguration.locale!.languageCode),
const SizedBox(
height: 10,
),
if (staticConfiguration.locale!.countryCode != null)
Text(T.screens.healthInfo.data.activeCountryCode + ': ' + staticConfiguration.locale!.countryCode!),
],
],
);
}
Upvotes: 1