I'm trying to select nested listview item, used listbuilder inside a listbuilder. Let’s say, I have 10 customers with 5 orders each, want to display customer1 and 5 orders, customer2 all orders…. .
I read the records from PostgreSQL and group them based on cust_id, resulted dataType is Map<String, List> and able to display on the screen. However while selecting the orders for processing (order2 of customer1, order4 of customer2, etc) it is throwing "Unsupported operation: Cannot modify an unmodifiable list".
Looks like I’m trying to modify immutable object. I tried to change Map<String, List> to Map<String, List< DetailedPositions>> but facing another error. Can you help to select orders. Any help would be appreciated.
import 'package:flutter/material.dart';
import 'package:e2/Models/model_positions.dart';
import 'package:e2/pages/modify_order.dart';
class ModifyPosition extends StatefulWidget {
const ModifyPosition({super.key});
#override
State<ModifyPosition> createState() => _ModifyPositionState();
}
class _ModifyPositionState extends State<ModifyPosition> {
Map data = {};
List<DetailedPositions> selectedContacts = [];
List<GroupedPositions> detailedPositions = [];
#override
Widget build(BuildContext context) {
data = ModalRoute.of(context)?.settings.arguments as Map;
var custIDs = data.values.toList()[0];
var custNames = data.values.toList()[1];
return Scaffold(
appBar: AppBar(
title: Text('Modify Positions '),
centerTitle: true,
),
body: SafeArea(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: FutureBuilder<Map<String, List<dynamic>>>(
future: ModelsPositions().detailedPositionsData(custIDs),
builder: (context, snapshot) {
Map<String, List<dynamic>> positions_raw =
snapshot.data ?? {};
List<GroupedPositions> detailedPositions = [];
positions_raw.forEach((k, v) {
detailedPositions
.add(GroupedPositions(custID: k, custInfo: v));
});
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
if (snapshot.hasError) {
return Center(
child: Text(
'Error while loading Master Positions screen'));
} else {
return DetailedPosition(detailedPositions);
}}},)),],),),),);}
Widget DetailedPosition(List<GroupedPositions> detailedPositions) {
return Container(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
child: ListView.builder(
shrinkWrap: true,
physics: BouncingScrollPhysics(),
itemCount: detailedPositions.length,
itemBuilder: (BuildContext context, int index) {
final pos = detailedPositions[index];
final ID = pos.custID;
final custInfo = pos.custInfo;
final custID = ID.split(',')[0];
final custName = ID.split(',')[1];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
//Text('Parent'),
Row(children: [
Expanded(
child: Padding(
padding: EdgeInsets.all(20),
child: Text("Cust_id : $custID",
style: TextStyle(
height: 3.0,
fontSize: 15.0,
fontWeight: FontWeight.bold,
)))),
Expanded(
child: Text("Cust_Name : $custName",
style: TextStyle(
height: 3.0,
fontSize: 15.0,
fontWeight: FontWeight.bold,
))),
]),
ListView.builder(
itemCount: custInfo.length,
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int childIndex) {
final pos = custInfo[childIndex];
final quantity = pos.elementAt(2);
final instrument = pos.elementAt(3);
final avgPrice = pos.elementAt(4);
final prodType = pos.elementAt(5);
final price1 = pos.elementAt(6);
final price2 = pos.elementAt(7);
final isSelected = pos.elementAt(8);
return ListTile(
horizontalTitleGap: -5,
title: Card(
child: Row(
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: [
Text('Qty : $quantity'),
const SizedBox(height: 10),
Text(instrument),
const SizedBox(height: 10),
Text('Avg : $avgPrice'),
],
),
),
Flexible(
fit: FlexFit.tight,
child: Column(
mainAxisAlignment:
MainAxisAlignment.end,
crossAxisAlignment:
CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
Text('$prodType'),
Text('$price1'),
const SizedBox(height: 10,),
Text('$price2'), ], ), ), ])),
leading: isSelected
? Icon(
Icons.check_circle,
color: Colors.green[700],
)
: Icon(
Icons.check_circle_outline,
color: Colors.grey,
),
onTap: () {
setState(() {
detailedPositions[index].custInfo[childIndex][8] =
!detailedPositions[index].custInfo[childIndex][8]; **--> Here facing the issue**
// if (detailedPositions[index]
// .custInfo[childIndex][8] ==
// true) {
.....
});
},); }), ], ); }), ) ], ), ); } }
class DetailedPositions {
String custID;
String custName;
int quantity;
String instrument;
double avgPrice;
String prodType;
double price1;
double price2;
bool isSelected = false;
DetailedPositions(
{required this.custID,
required this.custName,
required this.quantity,
required this.instrument,
required this.avgPrice,
required this.prodType,
required this.price1,
required this.price2});
}
class GroupedPositions {
String custID;
List<dynamic> custInfo;
GroupedPositions({required this.custID, required this.custInfo});
}
Related
My app reads data from PostgreSQL and displays on the screen in a listview. While selecting an item from listview app is getting refreshed and items are appending to existing list. My intention is to read data only once from the DB, display in list view, select single/multiple and proceed with processing. Any suggestions would be appriciated.
import 'package:e2/Models/MasterPositions.dart';
import 'package:flutter/material.dart';
import 'package:e2/Models/model_positions.dart';
class MasterControl extends StatefulWidget {
const MasterControl({super.key});
#override
State<MasterControl> createState() => _MasterControlState();
}
class _MasterControlState extends State<MasterControl> {
List<MasterPositions> selectedContacts = [];
List<MasterPositions> fetchedPositions = [];
List<MasterPositions> positions = [];
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Master Control"),
centerTitle: true,
automaticallyImplyLeading: false,
),
body: SafeArea(
child: Column(
children: [
Expanded(
child: FutureBuilder<List<dynamic>>(
future: ModelsPositions().fetchPositionsData(),
builder: (context, snapshot) {
List<dynamic> positionsRaw = snapshot.data ?? [];
for (var pos in positionsRaw) {
positions.add(MasterPositions(
custID: pos[0],
custName: pos[1],
mtm: double.tryParse(pos[2]) ?? 0.0,
availableCash: double.tryParse(pos[3]) ?? 0.0,
));
}
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(child: CircularProgressIndicator());
default:
if (snapshot.hasError) {
return const Center(
child: Text(
'Error while loading Master Positions screen'));
} else {
return buildPositions(positions);
}
}
},
)),
],
),
),
);
}
Widget buildPositions(List<dynamic> positions) {
return ListView.builder(
physics: BouncingScrollPhysics(),
itemCount: positions.length,
itemBuilder: (context, index) {
final pos = positions[index];
final custID = pos.custID;
final custName = pos.custName;
final mtm = pos.mtm;
final availableCash = pos.availableCash;
final isSelected = pos.isSelected;
return ListTile(
horizontalTitleGap: -5,
title: Card(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(custID),
const SizedBox(height: 5),
Text(custName)
],
),
),
Flexible(
fit: FlexFit.tight,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('MTM : $mtm',
softWrap: false,
style: const TextStyle(fontFamily: 'Roboto')),
const SizedBox(height: 10),
Text(
'Available : $availableCash',
softWrap: false,
),
const SizedBox(
height: 10,
),
],
),
),
],
),
),
leading: isSelected
? Icon(
Icons.check_circle,
color: Colors.green[700],
)
: const Icon(
Icons.check_circle_outline,
color: Colors.grey,
),
onTap: () {
setState(() {
positions[index].isSelected = !positions[index].isSelected;
if (positions[index].isSelected == true) {
selectedContacts.add(MasterPositions(
custID: custID,
custName: custName,
mtm: mtm,
availableCash: availableCash));
} else if (positions[index].isSelected == false) {
selectedContacts.removeWhere(
(element) => element.custID == positions[index].custID);
}
});
},
);
});
}
}
I often use something like this:
if (snapshot.hasData) {
if (snapshot.data!.isNotEmpty) {
List<dynamic> positionsRaw = snapshot.data ?? [];
for (var pos in positionsRaw) {
positions.add(MasterPositions(
custID: pos[0],
custName: pos[1],
mtm: double.tryParse(pos[2]) ?? 0.0,
availableCash: double.tryParse(pos[3]) ?? 0.0,
));
return buildPositions(positions);
} else {
// return something
}
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
And so you can guarantee that your widget will be built with data, and only be rebuilt when your new data it's loaded
I don't know why when I build my project, no error are return but my listview is empty..
The class :
final LocationService service = LocationService();
late Future<List<Location>> _locations;
#override
void initState() {
super.initState();
_locations = service.getLocations();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Mes locations'),
),
bottomNavigationBar: const BottomNavBar(2),
body: Center(
child: FutureBuilder<List<Location>>(
future: _locations,
builder:
(BuildContext context, AsyncSnapshot<List<Location>> response) {
List<Widget> children;
if (response.hasData) {
children = <Widget>[
ListView.builder(
itemCount: response.data?.length,
itemBuilder: (context, index) =>
_BuildRow(response.data?[index]),
itemExtent: 285,
),
];
} else if (response.hasError) {
children = <Widget>[
const Icon(
Icons.error_outline,
color: Colors.red,
size: 40,
),
const Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Un problème est survenu'),
),
];
} else {
children = const <Widget>[
SizedBox(
width: 50,
height: 50,
child: CircularProgressIndicator(
strokeWidth: 6,
),
),
];
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: children,
),
);
}),
),
);
}
// ignore: non_constant_identifier_names
_BuildRow(Location? location) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
children: [
Text(
"OKOK",
style: LocationTextStyle.priceBoldGreyStyle,
),
Text("${location?.dateFin}")
],
),
Text("${location?.montanttotal}€")
],
)
],
);
}
I have print my response.data?.length and it not empty.
At first it gave me the error "has size" but now the debug console is empty too...
You can find my project on GitLab : https://gitlab.com/victor.nardel/trash-project-flutter
Thank you in advance for your help
the error is caused by ListView.builder
simple solution:
wrap your ListView with Expanded
if (response.hasData) {
children = <Widget>[
Expanded(
child:ListView.builder(
....
for better code: just return the Widget, not List of widget.
something like below:
if(hasdata) return Listview();
else if(has error) return Column();
else return CircularIndicator();
so you can avoid redundant Widget.
Listview builder in Future builder is not scrollable.
order_screen.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../models_providers/order.dart';
import '../../services/global_methods.dart';
import 'empty_order.dart';
import 'full_order.dart';
class OrderScreen extends StatefulWidget {
static const routeName = '/Order-screen';
const OrderScreen({Key? key}) : super(key: key);
#override
State<OrderScreen> createState() => _OrderScreenState();
}
class _OrderScreenState extends State<OrderScreen> {
#override
void initState() {
super.initState();
}
GlobalMethods globalMethods = GlobalMethods();
#override
Widget build(BuildContext context) {
final orderProvider = Provider.of<OrderProvider>(context);
return FutureBuilder(
future: orderProvider.fetchOrders(),
builder: (context, snapshot) {
if (orderProvider.getOrders.isEmpty) {
return const Scaffold(
body: EmptyOrder(),
);
} else {
return Scaffold(
appBar: AppBar(
title: Text('Order (${orderProvider.getOrders.length})'),
),
body: SingleChildScrollView(
child: Column(
children: [
ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: orderProvider.getOrders.length,
itemBuilder: (ctx, i) {
return ChangeNotifierProvider.value(
value: orderProvider.getOrders[i],
child: const FullOrder(),
);
},
),
],
),
),
);
}
});
}
}
full_order.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class FullOrder extends StatelessWidget {
const FullOrder({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final FirebaseAuth _auth = FirebaseAuth.instance;
User? user = _auth.currentUser;
var _uid = user!.uid;
// it enable scrolling on small device
return FutureBuilder(
future: FirebaseFirestore.instance.collection('orders')
.where('userId', isEqualTo: _uid)
.get(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done ||
snapshot.hasData) {
List<QueryDocumentSnapshot> docs = (snapshot.data! as QuerySnapshot).docs;
return ListView.builder(
shrinkWrap: true,
itemCount: docs.length,
itemBuilder: (context, index) {
var data = docs[index];
var dataOrders = data['orders'] as List<dynamic>;
var mappedDataOrders = dataOrders.map((o) => (o as Map<String, dynamic>)).toList();
return Card(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Order Id: ${data['orderId']}'),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Order Date: ${DateFormat('dd/MM/yyyy').format(data['createdAt'].toDate())}'),
Text('Order Time: ${DateFormat('hh:mm a').format(data['createdAt'].toDate())}'),
],),
),
Padding(
padding: const EdgeInsets.all(4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Name: ${data['name']}', style: const TextStyle(fontWeight: FontWeight.bold),),
Text('Contact: ${data['phoneNumber'].toString()}', style: const TextStyle(fontWeight: FontWeight.bold),),
],
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Delivery Slot', style: TextStyle(fontWeight: FontWeight.bold),),
Text('${data['deliverySlot']}'),
],
),
Column(
children: [
Text('Address: ${data['addressType']}', style: const TextStyle(fontWeight: FontWeight.bold),),
Text('${data['address']}'),
Text('${data['area']}'),
Text('${data['city']}')
],
)
],
),
...List.generate(
mappedDataOrders.length,
(index) {
var order = mappedDataOrders[index];
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(order['title']),
Text(order['unit'].toString()),
Text(order['quantity'].toString()),
Text(order['price'].toString()),
],
),
],
);
}
)
]
)
);
}
);
}
return const Center(
child: SizedBox(
width: 100,
height: 100,
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.black),
strokeWidth: 5
)
)
);
}
);
}
}
I was able to recreate the error that is happening, you just need to remove the Column and the SingleChildScrollView and then you can scroll from the ListView,
another solution would be to disable the scrolling from the ListView and scroll using the SingleChildScrollView , and you can disable scrolling from ListView by adding:
physics: NeverScrollableScrollPhysics()
SingleChildScrollView and Column is not need, ListView is scrollable itself.
I am iterating over the map and creating a radio button inside the bottom sheet, in my dashboard I have "..." button once clicked it opens the bottom sheet.(inside my dashboard I am calling the DashboardFilter class present in filter.dart file that brings the bottomsheet,
below is the filter.dart file
class DashboardFilter extends StatefulWidget {
#override
_DashboardFilterState createState() => _DashboardFilterState();
final ValueChanged<Map<String, bool>> parentAction;
final ValueChanged<Map<String, bool>> childAction;
int radioValue1 = -1;
DashboardFilter(
{Key key,
this.parentAction,
this.childAction,
})
: super(key: key);
}
class _DashboardFilterState extends State<DashboardFilter> {
void showModalSheet() {
List<Map<String, Object>> timeData;
timeData = [
{"id": 1, "displayId": "Daily"},
{"id": 2, "displayId": "Weekly"},
{"id": 3, "displayId": "Monthly"}
];
showModalBottomSheet<void>(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter state) {
return createBox(context, timeData, state);
});
});
}
createBox(BuildContext context,
List<Map<String, Object>> tickbox, StateSetter state) {
DateTime toDate;
DateTime fromDate;
var tickboxdata = tickbox.map<Widget>((data) {
int id = data["id"];
var dispId = data["displayId"];
return radiogen(context, id, dispId);
}).toList();
return SingleChildScrollView(
child: LimitedBox(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
child: Column(
children: tickboxdata,
),),], ), ), );
}
void _handleRadioValueChange1(int value) {
setState(() {
widget.radioValue1 = value;
switch (widget.radioValue1) {
case 1:
print("1 selected");
break;
case 2:
break;
case 3:
break;
}
});
}
Widget radiogen(BuildContext context, int id, var disp) {
return Container(
padding: EdgeInsets.all(8.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Radio(
value: id,
groupValue: widget.radioValue1,
onChanged: _handleRadioValueChange1,
),
new Text(
disp,
style: new TextStyle(fontSize: 16.0),
), ], ), ], ), );
}
#override
Widget build(BuildContext context) {
return Container(
child: IconButton(
icon: new Icon(Icons.more_horiz), onPressed: showModalSheet),
);
}
}
The issue what I am facing is once I select the radio button, its not showing which Item is selected on that time ,I again need to go to back and press again (...) button then it shows that option is selected.
can anyone help me to solve this,
Ithink problem is with the setState, let me know if there any changes to do?
after sometime studying your code here is my solution
you need put a Statesetter on radiogen widget
Widget radiogen(BuildContext context, int id, var disp, StateSetter
stateSetter) {
return Container(
padding: EdgeInsets.all(8.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Radio(
value: id,
groupValue: widget.radioValue1,
onChanged: (value) {
stateSetter(() {
_handleRadioValueChange1(value);
});
}),
new Text(
disp,
style: new TextStyle(fontSize: 16.0),
),
],
),
],
),
);
}
then pass the state when you call the widget
createBox(BuildContext context, List<Map<String, Object>> tickbox,
StateSetter state) {
DateTime toDate;
DateTime fromDate;
var tickboxdata = tickbox.map<Widget>((data) {
int id = data["id"];
var dispId = data["displayId"];
return radiogen(context, id, dispId, state);
}).toList();
return SingleChildScrollView(
child: LimitedBox(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
child: Column(
children: tickboxdata,
),
),
],
),
),
);
}
this way you can change the state on the radio
Radio(
value: id,
groupValue: widget.radioValue1,
onChanged: (value) {
stateSetter(() {
_handleRadioValueChange1(value);
});
}),
I have 2 class, the first one is Main Widget Screen and 2nd is List Card. In View Perspective, Main Widget Screen Class contain List Widget from List Card Widget Class , as seen as below picture :
On List Card Widget, every list has 2 Text: Text1 and Text2.
also, there're 2 button: this month and last month. data on List Card Widget will refresh according to Bloc Stream ZikirHistoryBloc.onChangeTab(). the default is this month.
What i want to achieve is :
How to Show/Hide (On/Off) Text1 and Text2 by button on Main Screen ?
My Effort so far:
Main Screen Class :
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() {
return new _MainScreenState();
}
}
class _MainScreenState extends State<MainScreen> {
ZikirHistoryBloc zikirHistoryBloc;
int selectedValue = 1;
...
#override
Widget build(BuildContext context) {
return Scaffold(
...
Expanded(
child: ButtonMenuWidget(
image: "assets/images/zikir/button-text1.png",
title: translations.text("button_show_hide_text1"),
subtitle: point,
onTap: () {
//This is action for Button for Show/Hide Text1**
}
)
),
Expanded(
child: ButtonMenuWidget(
image: "assets/images/home/button-text2.png",
title: translations.text("button_show_hide_text2"),
subtitle: dzikir,
onTap: () {
//This is action for Button for Show/Hide Text1**
}
)
),
...
SizedBox(
width: 1000,
child: Padding(
padding: EdgeInsets.all(8),
child: StreamBuilder(
stream: zikirHistoryBloc.tabSelectedValueStream,
builder: (context, snapshot) {
return CupertinoSegmentedControl<int>(
selectedColor: Pigment.fromString(UIData.primaryColor),
borderColor: Pigment.fromString(UIData.primaryColor),
children: <int, Widget>{
0: Text(translations.text("last_month").toString()),
1: Text(translations.text("this_month").toString()),
},
onValueChanged: (int newValue) {
zikirHistoryBloc.onChangeTab(newValue);
},
groupValue: snapshot.data,
);
}
),
)
),
...
this.HistoriesWidget(),
...
}//::end of Widget build
...
Widget HistoriesWidget() {
return StreamBuilder(
stream: zikirHistoryBloc.dzikirHistoriesStream,
builder: (BuildContext ctx, AsyncSnapshot<List<DzikirHistory>> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
if (!snapshot.hasData) return Center(child: ActivityIndicatorWidget());
if (snapshot.data.length <= 0) return Center(child: Text(translations.text("empty_data")));
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
primary: false,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (ctx, i) {
//**Below will call class ListCardWidget**
return ListCardWidget(dzikirHistory: snapshot.data[i]);
}
);
}
);
}
...
} //::end of Class `Main Screen`
List Card Widget Class :
class ListCardWidget extends StatefulWidget {
DzikirHistory dzikirHistory;
ListCardWidget({this.dzikirHistory});
#override
_ListCardWidget createState() {
return new _ListCardWidget(dzikirHistory: dzikirHistory);
}
}
class _ListCardWidget extends State<ListCardWidget> {
bool isHiddenText1;
bool isHiddenText2;
DzikirHistory dzikirHistory;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
isHiddenText1 ? Container() : this.Text1Row(context),
isHiddenText2 ? Container() : this.Text2Row(context),
SizedBox(height: 10,),
],
)
],
),
),
Divider(height: 2, color: Pigment.fromString(UIData.primaryColor),),
],
);
}
Widget Text1Row(context) {
return Column(
children: <Widget>[
SizedBox(height: 10,),
Row(
children: <Widget>[
SizedBox(child: Image.asset("assets/images/home/my-zikir-total.png"),height: 20, width: 20,),
SizedBox(width: 10,),
Text('Text 1 ='+getNumberFormat(dzikirHistory.dzikirTotal.toString()),
style: TextStyle(
fontSize: 16,
color: Pigment.fromString(UIData.primaryColor)
),
),
],
),
],
);
}
Widget Text2Row(context) {
return Column(
children: <Widget>[
SizedBox(height: 10,),
Row(
children: <Widget>[
SizedBox(child: Image.asset("assets/images/home/my-point.png"),height: 20, width: 20,),
SizedBox(width: 10,),
Text('Text 2 ='+getNumberFormat(dzikirHistory.counter.toString()),
style: TextStyle(
fontSize: 16,
color: Pigment.fromString(UIData.primaryColor)
),
),
],
),
],
);
}
getNumberFormat(String str) {
final f = new NumberFormat("#.###");
return str.replaceAll(f.symbols.GROUP_SEP, '');
}
...
}//end of class List Card Widget
Other Class (Model) :
class DzikirHistory {
static const CATEGORY_POINT = "point";
static const CATEGORY_DZIKIR = "dzikir";
int id;
int counter;
int dzikirTotal;
String dzikirDate;
dynamic status;
dynamic createdAt;
dynamic updatedAt;
List<DzikirDetail> dzikirDetails;
DzikirHistory({
this.id,
this.counter,
this.dzikirTotal,
this.dzikirDate,
this.status,
this.createdAt,
this.updatedAt,
this.dzikirDetails
});
factory DzikirHistory.fromJson(Map<String, dynamic> json) => new DzikirHistory(
id: json["id"],
counter: json["counter"],
dzikirTotal: json["dzikir_total"],
dzikirDate: json["dzikir_date"],
status: json["status"],
createdAt: DateTime.parse(json["created_at"]),
updatedAt: DateTime.parse(json["updated_at"]),
// dzikirDetails: new List<DzikirDetail>.from(json["dzikir_details"].map((x) => DzikirDetail.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"id": id,
"counter": counter,
"dzikir_total": dzikirTotal,
"dzikir_date": dzikirDate,
"status": status,
"created_at": createdAt.toIso8601String(),
"updated_at": updatedAt.toIso8601String(),
// "dzikir_details": new List<dynamic>.from(dzikirDetails.map((x) => x.toJson())),
};
}
Bloc Code:
class ZikirHistoryBloc extends Object implements BlocBase {
...
init() async {
_tabSelectedValueController.sink.add(1);
_userController.sink.add(User.fromJson(await Storage.getUser()));
}
onChangeTab(int segment) {
_tabSelectedValueController.sink.add(segment);
if (segment == 1) {
_dzikirHistoriesController.sink.add(dzikirHistories);
} else {
_dzikirHistoriesController.sink.add(dzikirLastHistories);
}
}
...
}
Any Idea ?
Thanks In Advance
UPDATE
Using #FadhliS solution not working because when i click last month the data wont refresh (previously it's normal). also, the show/hide only works if i click last month or this month button.
The best solution would be using inherited widget/provider/scopedModel plugins as you want to manage the state globally across many classes from parent to children.You should read about those and incorporate them into your project.
But you can also try this, it's a small fix but not the ideal solution
Create two booleans in your parent state class and pass it to your child list widget class. You can set state to those too
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() {
return new _MainScreenState();
}
}
class _MainScreenState extends State<MainScreen> {
ZikirHistoryBloc zikirHistoryBloc;
int selectedValue = 1;
//you don't have to declare false as bool is initialised false by default
bool showText1 = false;
bool showText2 = false;
#override
Widget build(BuildContext context) {
return Scaffold(
...
Expanded(
child: ButtonMenuWidget(
image: "assets/images/zikir/button-text1.png",
title: translations.text("button_show_hide_text1"),
subtitle: point,
onTap: () {
setState(){
showText1 = !showText1;
}
}
)
),
Expanded(
child: ButtonMenuWidget(
image: "assets/images/home/button-text2.png",
title: translations.text("button_show_hide_text2"),
subtitle: dzikir,
onTap: () {
setState(){
showText2 = !showText2;
}
}
)
),
...
this.HistoriesWidget(),
...
}//::end of Widget build
...
Widget HistoriesWidget() {
return StreamBuilder(
stream: zikirHistoryBloc.dzikirHistoriesStream,
builder: (BuildContext ctx, AsyncSnapshot<List<DzikirHistory>> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
if (!snapshot.hasData) return Center(child: ActivityIndicatorWidget());
if (snapshot.data.length <= 0) return Center(child: Text(translations.text("empty_data")));
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
primary: false,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (ctx, i) {
//**Below will call class ListCardWidget**
return ListCardWidget(
dzikirHistory: snapshot.data[i],
showText1: showText1,
showText2: showText2
);
}
);
}
);
}
...
} //
And then in your list card widget class, pass it as parameter and use it
class ListCardWidget extends StatefulWidget {
DzikirHistory dzikirHistory;
bool showText1;
bool showText2;
ListCardWidget({this.dzikirHistory, this.showText1, this.showText2});
#override
_ListCardWidget createState() {
//you dont have to pass the elements to your state class
return new _ListCardWidget();
}
}
class _ListCardWidget extends State<ListCardWidget> {
//remove all these variables
//you can access all the variables directly from the stateful class
//widget.yourvariable
bool isHiddenText1;
bool isHiddenText2;
DzikirHistory dzikirHistory;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
widget.showText1 ? Container() : this.Text1Row(context),
widget.showText2 ? Container() : this.Text2Row(context),
SizedBox(height: 10,),
],
)
],
),
),
Divider(height: 2, color: Pigment.fromString(UIData.primaryColor),),
],
);
}
Widget Text1Row(context) {
return Column(
children: <Widget>[
SizedBox(height: 10,),
Row(
children: <Widget>[
SizedBox(child: Image.asset("assets/images/home/my-zikir-total.png"),height: 20, width: 20,),
SizedBox(width: 10,),
Text('Text 1 ='+getNumberFormat(widget.dzikirHistory.dzikirTotal.toString()),
style: TextStyle(
fontSize: 16,
color: Pigment.fromString(UIData.primaryColor)
),
),
],
),
],
);
}
Widget Text2Row(context) {
return Column(
children: <Widget>[
SizedBox(height: 10,),
Row(
children: <Widget>[
SizedBox(child: Image.asset("assets/images/home/my-point.png"),height: 20, width: 20,),
SizedBox(width: 10,),
Text('Text 2 ='+getNumberFormat(widget.dzikirHistory.counter.toString()),
style: TextStyle(
fontSize: 16,
color: Pigment.fromString(UIData.primaryColor)
),
),
],
),
],
);
}
getNumberFormat(String str) {
final f = new NumberFormat("#.###");
return str.replaceAll(f.symbols.GROUP_SEP, '');
}
...
}//end of class List Card Widget