Change a value from a List item and mantain it when changing pages on a page view - flutter

I get the length of the data in a page view.builder and then generate a List with all values being false (quantity = data length):
List<bool> isLockedList = List.generate(dataLength, (index) => false);
On a GestureDetector, on onTap, I have:
onTap: () {
setState(() {
isLockedList[index] = true;
});
print(isLockedList);
}
When I click on the item index 0 from the page view builder, which the length is 2, the List changes to [true, false]. When I go index 1, it changes to [false, true]. Why isn't it changing to [true, true]. Wasn't the value supposed to be overwritten?
When I change pages, without using the onTap, the List is reset to default values [false,false]
Entire code:
late final Stream<QuerySnapshot> _usersStream = FirebaseFirestore.instance
.collection('systems')
.doc(widget.systemRef)
.collection('matters')
.doc(widget.matterRef)
.collection('questions')
.snapshots();
#override
Widget build(BuildContext context) {
mediaQuery = MediaQuery.of(context).size;
int totalQuestion = 0;
int currentQuestion = 0;
return StreamBuilder<QuerySnapshot>(
stream: _usersStream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Scaffold(
body: Center(
child: Text('Algo deu errado'),
),
);
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
int dataLength = snapshot.data!.docs.length;
List<bool> isLockedList = List.generate(dataLength, (index) => false);
return Scaffold(
appBar: AppBar(
shadowColor: Colors.transparent,
backgroundColor: Colors.white,
leading: Navigator.canPop(context)
? IconButton(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
icon: const Icon(
Icons.chevron_left,
color: Colors.black,
size: 40,
),
onPressed: () => Navigator.of(context).pop(),
)
: null,
actions: [
Padding(
padding: EdgeInsets.only(right: mediaQuery.width *0.05),
child: Center(child: Text('$currentQuestion /$totalQuestion', style: const TextStyle(color: Colors.black),)),
),
],
),
body: SafeArea(
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.white,
child: PageView.builder(
onPageChanged: (value) {
print(isLockedList);
},
controller: _pageController,
itemCount: dataLength,
itemBuilder: (ctx, index) {
refIndex = index;
currentQuestion = index + 1;
totalQuestion = dataLength;
QueryDocumentSnapshot<Object?> output =
snapshot.data!.docs[index];
List<String> alternativesCode = List<String>.generate(
output['numberOfOptions'],
(int index) => 'option${index + 1}.code');
List<String> alternativesText = List<String>.generate(
output['numberOfOptions'],
(int index) => 'option${index + 1}.text');
imageUrl = output['image'];
return Container(
width: double.infinity,
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width * 0.05),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// SizedBox(
// width: mediaQuery.width,
// child: Text(
// '${index + 1}/$dataLength',
// textAlign: TextAlign.end,
// style: const TextStyle(
// fontSize: 18, fontWeight: FontWeight.w600),
// ),
// ),
Container(
constraints: BoxConstraints(
maxHeight: mediaQuery.height * 0.16,
minHeight: mediaQuery.height * 0.05),
child: AutoSizeText(
output['title'],
maxLines: 4,
textAlign: TextAlign.start,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 23),
),
),
SizedBox(height: mediaQuery.height * 0.03),
const SizedBox(
height: 10,
),
imageUrl.isNotEmpty
? Container(
alignment: Alignment.center,
child: Image.network(imageUrl,
height: mediaQuery.height * 0.2))
: SizedBox(
height: mediaQuery.height * 0.05,
),
SizedBox(
height: mediaQuery.height * 0.03,
),
Container(
constraints:
BoxConstraints(maxHeight: mediaQuery.height * 0.1, maxWidth: mediaQuery.width * 0.9),
child: AutoSizeText(
output['subtitle'],
maxLines: 2,
textAlign: TextAlign.start,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
fontStyle: FontStyle.italic),
),
),
SizedBox(
height: mediaQuery.height * 0.02,
),
Container(
height: mediaQuery.height * 0.37,
width: double.infinity,
color: Colors.white,
child: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
itemCount: alternativesText.length,
itemBuilder: (ctx, i) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5),
child: GestureDetector(
child: Container(
height: mediaQuery.height * 0.07,
decoration: BoxDecoration(
boxShadow: const [
BoxShadow(
color: Colors.grey,
blurRadius: 3,
// Shadow position
),
],
border: Border.all(
width: 3, color: Colors.grey),
borderRadius: BorderRadius.circular(10),
color: Colors.grey[100],
),
// width: mediaQuery.width,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10),
child: Row(
children: [
Text(
'${output[alternativesCode[i]]}',
style: TextStyle(
color: Colors.grey.shade800,
fontWeight: FontWeight.w600,
fontSize: 20),
),
Container(
padding:
const EdgeInsets.only(left: 5),
alignment: Alignment.centerLeft,
width: mediaQuery.width * 0.78,
height: mediaQuery.height * 0.07,
// constraints: BoxConstraints(maxWidth: mediaQuery.width * 0.8, minWidth: mediaQuery),
child: AutoSizeText(
output[alternativesText[i]],
maxLines: 2,
textAlign: TextAlign.justify,
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.w500,
fontSize: 15),
),
),
],
),
),
),
onTap: () {
setState(() {
isLockedList[index] = true;
});
print(isLockedList);
}),
);
},
),
),
],
),
);
},
),
),
),
);
},
);
}
}

Follow these steps.
Declare isLockedList outside the build method as a nullable.
Set isLockedList only when the value is null in the StreamBuilder.
Code:
List<bool>? isLockedList;
#override
Widget build(BuildContext context){
....
return StreamBuilder<QuerySnapshot>(
builder: (context, snapshot) {
...
if (snapshot.hasError) {
return const Scaffold(
body: Center(
child: Text('Algo deu errado'),
),
);
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
int dataLength = snapshot.data!.docs.length;
if(isLockedList == null){
isLockedList = List.generate(dataLength, (index) => false);
}
....
}
);
....
}

Related

Flutter : Get Value of Selected Container

I am trying to get the Text value of the selected container in a GridView. I am able to print the selected index. But I need to get the text value of the selected container so that I can save it in sharedPreferences and later get it and store it on the server.
Please assist. Your help will be gladly appreciated.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class QuickSurveyScreen extends StatefulWidget {
const QuickSurveyScreen({super.key});
#override
State<QuickSurveyScreen> createState() => _QuickSurveyScreenState();
}
class _QuickSurveyScreenState extends State<QuickSurveyScreen> {
int selectedIndex = -1;
List<String> gasUsage = [
"Heater",
"Stove",
"Geyser",
"Fireplace",
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return [
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(top: 120.0, right: 30, left: 30),
child: Column(
children: const [
Text(
"What do you use \nLP Gas for?",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold),
),
Padding(
padding: EdgeInsets.symmetric(
vertical: 16.0,
horizontal: 20,
),
child: Text(
"Please choose one or more options from below",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
),
),
),
],
),
))
];
},
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: [
Expanded(
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.0,
crossAxisSpacing: 20.0,
mainAxisSpacing: 20.0,
),
physics: const NeverScrollableScrollPhysics(),
itemCount: gasUsage.length,
itemBuilder: (BuildContext context, int index) {
return gasUsageContainer("", gasUsage[index], index);
}),
),
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 65.0),
backgroundColor: const Color(0xFFF0A202),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(26.0),
),
textStyle: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
child: const Text('Continue'),
),
],
),
),
),
),
);
}
gasUsageContainer(String image, String name, int index) {
return GestureDetector(
onTap: () {
setState(() {
if (selectedIndex == index) {
selectedIndex = -1;
} else {
selectedIndex = index;
}
if (kDebugMode) {
print(selectedIndex);
}
});
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: selectedIndex == index
? Colors.amber.shade50
: Colors.grey.shade200,
border: Border.all(
color: selectedIndex == index
? Colors.amber
: Colors.blue.withOpacity(0),
width: 2.0,
),
borderRadius: BorderRadius.circular(22.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: selectedIndex == index
? Colors.amber.shade800
: Colors.black,
),
)
]),
),
);
}
}
You can get it from List with index. Checkout the change I did for onTap in gasUsageContainer
onTap: () {
setState(() {
String? selected;
if (selectedIndex == index) {
selectedIndex = -1;
selected = null;
} else {
selectedIndex = index;
selected = gasUsage[index];//Here you get the value
}
if (kDebugMode) {
print('selectedIndex:$selectedIndex, selected: $selected');
}
});
}
Edited
class _QuickSurveyScreenState extends State<QuickSurveyScreen> {
final selectedItems = <String>[];
...
gasUsageContainer(String image, String name, int index) {
String item = gasUsage[index];
return GestureDetector(
onTap: () {
setState(() {
if (selectedItems.contains(item)) {
selectedItems.remove(item);
} else {
selectedItems.add(item);
}
if (kDebugMode) {
print('selectedItems: $selectedItems');
}
});
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: selectedItems.contains(item)
? Colors.amber.shade50
: Colors.grey.shade200,
border: Border.all(
color: selectedItems.contains(item)
? Colors.amber
: Colors.blue.withOpacity(0),
width: 2.0,
),
borderRadius: BorderRadius.circular(22.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: selectedItems.contains(item)
? Colors.amber.shade800
: Colors.black,
),
)
]),
),
);
}
}

How do i return a container based on a list of item selected in flutter?

I have a list of items
List<String> items = [
"All",
"Jobs",
"Messages",
"Customers",
];
int current = 0;
And this list is directly responsible for my tab bar:
When i tap an item in the Tab bar i want to return a different container on each of them?
How do i go about this in flutter?
I tried returning an if statement just before the container but it seems i don't get the statement correctly.
this is the container i want to return if the item user select is All, and then put conditions in place for the rest items.
this is how i put the condition but it gives me this error
My return statement and code -
current = 0 ??
Container(
margin: const EdgeInsets.only(top: 30),
height: MediaQuery.of(context).size.height * 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 10,
),
Text(
items[current],
style: GoogleFonts.laila(
fontWeight: FontWeight.w500,
fontSize: 30,
color: Colors.deepPurple),
),
],
),
),
current = 1 ?? Text('hello')
FULL WIDGET ADDED
class NotificationsView extends StatefulWidget {
#override
State<NotificationsView> createState() => _NotificationsViewState();
}
class _NotificationsViewState extends State<NotificationsView> {
final controller = Get.put(NotificationsController());
List<String> items = [
"All",
"Jobs",
"Messages",
"Customers",
];
int current = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Notifications".tr,
style: GoogleFonts.poppins(
color: Color(0xff000000),
fontSize: 16,
fontWeight: FontWeight.w600),
),
centerTitle: false,
backgroundColor: Colors.transparent,
elevation: 0,
automaticallyImplyLeading: false,
leadingWidth: 15,
leading: new IconButton(
icon: new Icon(Icons.arrow_back_ios, color: Color(0xff3498DB)),
onPressed: () => {Get.back()},
),
),
body: RefreshIndicator(
onRefresh: () async {
},
child: ListView(
primary: true,
children: <Widget>[
filter(),
],
),
),
);
}
Widget notificationsList() {
return Obx(() {
if (!controller.notifications.isNotEmpty) {
return CircularLoadingWidget(
height: 300,
onCompleteText: "Notification List is Empty".tr,
);
} else {
var _notifications = controller.notifications;
return ListView.separated(
itemCount: _notifications.length,
separatorBuilder: (context, index) {
return SizedBox(height: 7);
},
shrinkWrap: true,
primary: false,
itemBuilder: (context, index) {
var _notification = controller.notifications.elementAt(index);
if (_notification.data['message_id'] != null) {
return MessageNotificationItemWidget(
notification: _notification);
} else if (_notification.data['booking_id'] != null) {
return BookingNotificationItemWidget(
notification: _notification);
} else {
return NotificationItemWidget(
notification: _notification,
onDismissed: (notification) {
controller.removeNotification(notification);
},
onTap: (notification) async {
await controller.markAsReadNotification(notification);
},
);
}
});
}
});
}
Widget filter() {
return Container(
width: double.infinity,
margin: const EdgeInsets.all(5),
child: Column(
children: [
/// CUSTOM TABBAR
SizedBox(
width: double.infinity,
height: 60,
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: items.length,
scrollDirection: Axis.horizontal,
itemBuilder: (ctx, index) {
return Column(
children: [
GestureDetector(
onTap: () {
setState(() {
current = index;
});
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
margin: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: current == index
? Color(0xff34495E)
: Color(0xffF5F5F5),
borderRadius: BorderRadius.circular(11),
),
child: Center(
child: Padding(
padding: const EdgeInsets.only(
left: 10.0, right: 10.0, top: 5, bottom: 5),
child: Text(
items[index],
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: current == index
? Colors.white
: Colors.grey),
),
),
),
),
),
],
);
}),
),
/// MAIN BODY
current = 0 ??
Container(
margin: const EdgeInsets.only(top: 30),
height: MediaQuery.of(context).size.height * 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 10,
),
Text(
items[current],
style: GoogleFonts.laila(
fontWeight: FontWeight.w500,
fontSize: 30,
color: Colors.deepPurple),
),
Padding(
padding: const EdgeInsets.only(
left: 20.0, right: 20.0, top: 20.0, bottom: 20),
child: Column(
children: [
Stack(
children: [
Row(
children: [
Container(
decoration: BoxDecoration(
color: Color(0xffEFFAFF),
borderRadius:
BorderRadius.circular(20)),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Image.asset(
'assets/icon/suitcase.png'),
),
),
SizedBox(
width: 15,
),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'New Job started ',
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xff151515)),
),
Text(
'Tailoring for John Cletus ',
style: GoogleFonts.poppins(
fontSize: 10,
fontWeight: FontWeight.w400,
color: Color(0xff151515)),
),
],
),
Spacer(),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2),
color: Color(0xffFFE8E8),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Urgent',
style: GoogleFonts.poppins(
color: Color(0xffC95E5E)),
),
),
),
],
),
],
),
Divider(
height: 5,
color: Color(0xffEFFAFF),
),
],
),
),
],
),
),
current = 1 ?? Text('hello')
],
),
);
}
}
You can use Builder if want to display different type of widget based on the current index.
Builder(
builder: (context) {
switch (current) {
case 0:
return Container(
margin: const EdgeInsets.only(top: 30),
height: MediaQuery.of(context).size.height * 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 10,
),
Text(
items[current],
style: GoogleFonts.laila(
fontWeight: FontWeight.w500,
fontSize: 30,
color: Colors.deepPurple),
),
],
),
);
case 1:
return Text('Hello');
default:
return SizedBox.shrink();
}
},
);
Below approach will solve your problem. If you need further assistance, please feel free to comment.
int _currentIndex = 0;
var _containers = <Widget>[
AllContainer(),
JobsContainer(),
MessagesContainer(),
CustomerContainer(),
];
Widget _bottomTab() {
return BottomNavigationBar(
currentIndex: _currentIndex,
onTap: _onItemTapped, //
type: BottomNavigationBarType.fixed,
selectedLabelStyle: const TextStyle(color: Colors.blue),
selectedItemColor: WAPrimaryColor,
unselectedLabelStyle: const TextStyle(color: Colors.blue),
unselectedItemColor: Colors.grey,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(label: 'All'),
BottomNavigationBarItem(
label: 'Jobs'),
BottomNavigationBarItem(
label: 'Messages'),
BottomNavigationBarItem( label: 'Customer'),
],
);
}
void _onItemTapped(int index) async {
print('bottom index::: $index');
setState(() {
_currentIndex = index;
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
bottomNavigationBar: _bottomTab(),
body: Center(child: _containers.elementAt(_currentIndex)),
),
);
}
You can use conditional if on widget level,
like
/// MAIN BODY
if (current == 0)
Container(
margin: const EdgeInsets.only(top: 30),
height: MediaQuery.of(context).size.height * 1,
...
),
if (current == 1) Text('hello')
Also can be use else if
if (current == 0)
Container(
margin: const EdgeInsets.only(top: 30),
height: MediaQuery.of(context).size.height * 1,
) //you shouldnt put coma
else if (current == 1) Text('hello')
],
),
);
But creating a separate method will be better instead of putting it here
Widget getWidget(int index) {
/// MAIN BODY
if (current == 0) // or switch case
return Container(
margin: const EdgeInsets.only(top: 30),
height: MediaQuery.of(context).size.height * 1,
); //you shouldnt put coma
else if (current == 1) return Text('hello');
return Text("default");
}
And call the method getWidget(current).
Also there are some widget like PageView, IndexedStack will help to organize the code structure

Tab Bar in Flutter

I want to implement Tab Bar in my application having length 2 named "Need Help" and "Help Requests". In "Need Help" tab, I want my first container (i.e. Upload data to Firestore Database) and in "Help Requests" tab, I want my second container (i.e. Retrieve data from Firestore Database). I am new to flutter and will be very much grateful to you if you can help me.
Source code:
import 'package:chat_app/group_chats/group_info.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import '../constants.dart';
import '../global_data.dart';
class FinancialRoom extends StatelessWidget {
final String groupChatId, groupName;
FinancialRoom({required this.groupName, required this.groupChatId, Key? key})
: super(key: key);
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final FirebaseAuth _auth = FirebaseAuth.instance;
final _formKey = GlobalKey<FormState>();
TextEditingController amountValue = TextEditingController();
void onSend() async {
Map<String, dynamic> data = {
"amount": amountValue.text,
"sendBy": _auth.currentUser!.displayName,
"type": "text",
"time": FieldValue.serverTimestamp(),
};
amountValue.clear();
await _firestore
.collection('groups')
.doc(groupChatId)
.collection('chats')
.add(data);
}
#override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
return Scaffold(
appBar: AppBar(
title: Text(groupName),
actions: [
IconButton(
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => GroupInfo(
groupName: groupName,
groupId: groupChatId,
),
),
),
icon: Icon(Icons.more_vert)),
],
),
body: SafeArea(
child: ListView(padding: EdgeInsets.all(20.0), children: [
Container(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(
height: 10.0,
),
TextFormField(
controller: amountValue,
decoration: InputDecoration(
hintText: 'Enter the amount you want',
labelText: 'Enter the amount you want',
prefixIcon: Icon(Icons.account_balance_wallet_outlined),
enabledBorder: kEnabledBorder,
focusedBorder: kFocusedBorder,
errorBorder: kErrorBorder,
focusedErrorBorder: kErrorBorder,
),
onTap: () {
},
// The validator receives the text that the user has entered.
validator: (value) {
if (value!.isEmpty) {
return 'Please enter the amount you want';
}
return null;
},
),
SizedBox(
height: kInputSpacing,
),
SizedBox(
width: double.infinity,
child: FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
color: Colors.blue,
textColor: Colors.white,
padding: EdgeInsets.only(top: 16.0, bottom: 16.0),
onPressed: onSend,
child: Text(
'SEND',
style: kButtonTextStyle,
),
),
),
],
),
),
Container(
height: size.height / 1.27,
width: size.width,
child: StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('groups')
.doc(groupChatId)
.collection('chats')
.orderBy('time')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
Map<String, dynamic> data =
snapshot.data!.docs[index].data()
as Map<String, dynamic>;
return messageTile(size, data);
},
);
} else {
return Container();
}
},
),
),
]),
),
);
}
Widget messageTile(Size size, Map<String, dynamic> data) {
return Builder(builder: (_) {
if (data['type'] == "text") {
return Container(
width: size.width,
alignment: data['sendBy'] == _auth.currentUser!.displayName
? Alignment.centerRight
: Alignment.centerLeft,
child: Container(
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 14),
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: Colors.blue,
),
child: Column(
children: [
Text(
data['sendBy'],
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
SizedBox(
height: size.height / 200,
),
Text(
data['amount'],
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
],
)),
);
} else if (data['type'] == "img") {
return Container(
width: size.width,
alignment: data['sendBy'] == _auth.currentUser!.displayName
? Alignment.centerRight
: Alignment.centerLeft,
child: Container(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 14),
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 8),
height: size.height / 2,
child: Image.network(
data['amount'],
),
),
);
} else if (data['type'] == "notify") {
return Container(
width: size.width,
alignment: Alignment.center,
child: Container(
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 8),
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Colors.black38,
),
child: Text(
data['message'],
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
);
} else {
return SizedBox();
}
});
}
}
It's very straightforward to implement a simple TabBar in your app. All you need is a TabController and two widgets called TabBar and TabBarView. Here is a simple example:
DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(tabs: [
Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'),
]),
),
body: TabBarView(children: [
// Tab 1
Container(color: Colors.red),
// Tab 2
Container(color: Colors.blue),
]),
),
);
Now all you need to do is to replace children inside TabBarView with whatever you want to display.

I try to use Lifting State Up in Flutter, but I get "Closure call with mismatched arguments"

I pass Data from parent to child, and one of these parameters is a Fn. What I need is to return data from child to parent again, but I get an error as below:
[ Closure call with mismatched arguments: function '_ReservationBranchesSlotsScreenState._changeData' Receiver: Closure:
({String areaId, int coastPerPerson, int selectedBranchChecked, String
formatted, bool showCreateReservationButton, bool isExpanded, int
expandedIndex}) => void from Function '_changeData#176179245':. Tried
calling: _ReservationBranchesSlotsScreenState._changeData(areaId:
"d98a4e0e-d408-40c8-b387-9a405683a389", coastPerPerson: 0,
expandedIndex: -1, formatted: null, isExpanded: false,
selectedBrnachChecked: 0, showCreateReservationButton: false) Found:
_ReservationBranchesSlotsScreenState._changeData({String areaId, int coastPerPerson, int selectedBranchChecked, String formatted, bool
showCreateReservationButton, bool isExpanded, int expandedIndex}) =>
void ]
I create a Function in the parent Widget that do some actions, and pass this Fn to the child widget as below.
This is the parent widget Fn:
void _changeData({
String areaId,
int coastPerPerson,
int selectedBranchChecked,
String formatted,
bool showCreateReservationButton,
bool isExpanded,
int expandedIndex
}){
setState(() {
_areaId = areaId;
_coastPerPerson = coastPerPerson;
_selectedBranchChecked = selectedBranchChecked;
_formatted = formatted;
_showCreateReservationButton = showCreateReservationButton;
_isExpanded = isExpanded;
_expandedIndex = expandedIndex;
});
}
also below when I pass this Fn to the child widget:
SelectBranchWidget(
branches: _branches,
coastPerPerson: _coastPerPerson,
areaId: _areaId,
selectedBranchChecked: _selectedBranchChecked,
formatted: _formatted,
isExpanded: _isExpanded,
showCreateReservationButton: _showCreateReservationButton,
expandedIndex: _expandedIndex,
***changeData: _changeData,***
),
and here is the child widget which I need to return a Data from it to the parent again:
class SelectBranchWidget extends StatefulWidget {
List<RestaurantBranch> branches;
int coastPerPerson;
String areaId;
int selectedBranchChecked;
String formatted;
bool isExpanded;
bool showCreateReservationButton;
int expandedIndex;
Function changeData;
SelectBranchWidget(
{this.branches,
this.coastPerPerson,
this.areaId,
this.selectedBranchChecked,
this.formatted,
this.isExpanded,
this.showCreateReservationButton,
this.expandedIndex,
this.changeData,
});
#override
_SelectBranchWidgetState createState() => _SelectBranchWidgetState();
}
class _SelectBranchWidgetState extends State<SelectBranchWidget> {
void _changedValues(int i, int branchAreaIndex) {
widget.coastPerPerson = widget.branches[i].branchAreas[branchAreaIndex].costPerSeat;
widget.areaId = widget.branches[i].branchAreas[branchAreaIndex].guid;
print('areaId IS ${widget.areaId}');
widget.selectedBranchChecked = i;
widget.formatted = null;
widget.isExpanded = false;
widget.showCreateReservationButton = false;
widget.expandedIndex = -1;
widget.changeData(
areaId: widget.areaId,
coastPerPerson: widget.coastPerPerson,
selectedBrnachChecked:widget.selectedBranchChecked,
formatted: widget.formatted,
showCreateReservationButton:widget.showCreateReservationButton,
isExpanded:widget.isExpanded,
expandedIndex: widget.expandedIndex
);
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(Radius.circular(10))),
padding: EdgeInsets.all(8),
margin: EdgeInsets.only(left: 16, right: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
'Select Branch',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
),
SizedBox(
height: 8,
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(
'Number of branches = ${widget.branches.length}',
style: TextStyle(color: Colors.grey, fontSize: 10),
),
),
),
SizedBox(
height: 16,
),
ConstrainedBox(
constraints:
BoxConstraints(maxHeight: 230, maxWidth: double.infinity),
child: ListView.builder(
shrinkWrap: true,
itemCount: widget.branches.length,
itemBuilder: (ctx, i) {
return GestureDetector(
onTap: () {
if (widget.branches[i].branchAreas.length == 1) {
_changedValues(i, 0);
return;
}
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).backgroundColor,
title: Text(
'Select Area',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
content: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
// height: (_branches[i].branchAreas.length == 1)
// ?70
// : (_branches[i].branchAreas.length == 2)
// ? 100
// :150,
width: 100.0,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: 120),
child: ListView.builder(
shrinkWrap: true,
itemCount:
widget.branches[i].branchAreas.length,
itemBuilder:
(BuildContext context, int index) {
return GestureDetector(
onTap: () {
_changedValues(i, index);
Navigator.of(context).pop();
},
child: Padding(
padding: const EdgeInsets.only(
top: 16, left: 36.0, right: 36),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
widget.branches[i]
.branchAreas[index].name,
style: TextStyle(
color: Colors.white),
),
Divider(
color: Colors.grey,
thickness: 1,
),
],
),
),
);
},
),
),
),
);
});
},
child: Card(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
color: Theme.of(context).backgroundColor,
child: Container(
padding: EdgeInsets.all(20),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.branches[i].branchDistrict.name,
style: TextStyle(color: Colors.white, fontSize: 16),
),
(widget.selectedBranchChecked == i)
? Container(
height: 40,
width: 40,
child: Image.asset(
getAssetsName(AssetsImage.checkIcon),
fit: BoxFit.cover,
),
)
: Container(
padding: EdgeInsets.only(top: 5, bottom: 5),
height: 40,
width: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: SizedBox(),
),
],
),
),
),
);
},
),
),
],
),
);
}
}
You can use two approaches to achieve this.
User first one if you want to return from a route (from next screen to previous one)
Use second Approach if you want to make changes to Parent Widget from its child widget on same screen.
1st Approach (Returning an object from route)
Create a Model of data you are passing.
class MyModel {
int coastPerPerson;
String areaId;
int selectedBranchChecked;
String formatted;
bool isExpanded;
bool showCreateReservationButton;
int expandedIndex;
Function changeData;
MyModel({
this.areaId,
this.coastPerPerson,
this.selectedBranchChecked,
this.formatted ,
this.isExpanded,
this.showCreateReservationButton,
this.expandedIndex,
});
}
I'm assuming that You Parent Looks like this. And you're navigating from Parent Page to
SelectBranchWidget page
class YourParentWidget extends StatefulWidget {
YourParentWidget({Key key}) : super(key: key);
#override
_YourParentWidgetState createState() => _YourParentWidgetState();
}
class _YourParentWidgetState extends State<YourParentWidget> {
navigateAndChangeData(){
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return SelectBranchWidget();
})).then((value) {
///Use this to get value from next screen to PArent Screen
if(value != null) {
var model = value as MyModel;
//Now you have access to all returning values
//model.areaId
//model.coastPerPerson
//model.selectedBranchChecked
//...
///Make changes accordingly
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
///Your UI Conponents....
);
}
}
Now In SelectBranchWidget Widget, What you need to do in _changedValues function is to return model values. i.e.
class SelectBranchWidget extends StatefulWidget {
List<RestaurantBranch> branches;
int coastPerPerson;
String areaId;
int selectedBranchChecked;
String formatted;
bool isExpanded;
bool showCreateReservationButton;
int expandedIndex;
Function changeData;
SelectBranchWidget(
{this.branches,
this.coastPerPerson,
this.areaId,
this.selectedBranchChecked,
this.formatted,
this.isExpanded,
this.showCreateReservationButton,
this.expandedIndex,
this.changeData,
});
#override
_SelectBranchWidgetState createState() => _SelectBranchWidgetState();
}
class _SelectBranchWidgetState extends State<SelectBranchWidget> {
void _changedValues(int i, int branchAreaIndex) {
MyModel model = MyModel(
areaId: widget.branches[i].branchAreas[branchAreaIndex].guid,
coastPerPerson: widget.branches[i].branchAreas[branchAreaIndex].costPerSeat,
selectedBranchChecked: 1,
formatted: null,
isExpanded: false,
showCreateReservationButton: false,
expandedIndex: -1
);
Navigator.of(context).pop(model);
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(Radius.circular(10))),
padding: EdgeInsets.all(8),
margin: EdgeInsets.only(left: 16, right: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
'Select Branch',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
),
SizedBox(
height: 8,
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(
'Number of branches = ${widget.branches.length}',
style: TextStyle(color: Colors.grey, fontSize: 10),
),
),
),
SizedBox(
height: 16,
),
ConstrainedBox(
constraints:
BoxConstraints(maxHeight: 230, maxWidth: double.infinity),
child: ListView.builder(
shrinkWrap: true,
itemCount: widget.branches.length,
itemBuilder: (ctx, i) {
return GestureDetector(
onTap: () {
if (widget.branches[i].branchAreas.length == 1) {
_changedValues(i, 0);
return;
}
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).backgroundColor,
title: Text(
'Select Area',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
content: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
// height: (_branches[i].branchAreas.length == 1)
// ?70
// : (_branches[i].branchAreas.length == 2)
// ? 100
// :150,
width: 100.0,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: 120),
child: ListView.builder(
shrinkWrap: true,
itemCount:
widget.branches[i].branchAreas.length,
itemBuilder:
(BuildContext context, int index) {
return GestureDetector(
onTap: () {
_changedValues(i, index);
Navigator.of(context).pop();
},
child: Padding(
padding: const EdgeInsets.only(
top: 16, left: 36.0, right: 36),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
widget.branches[i]
.branchAreas[index].name,
style: TextStyle(
color: Colors.white),
),
Divider(
color: Colors.grey,
thickness: 1,
),
],
),
),
);
},
),
),
),
);
});
},
child: Card(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
color: Theme.of(context).backgroundColor,
child: Container(
padding: EdgeInsets.all(20),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.branches[i].branchDistrict.name,
style: TextStyle(color: Colors.white, fontSize: 16),
),
(widget.selectedBranchChecked == i)
? Container(
height: 40,
width: 40,
child: Image.asset(
getAssetsName(AssetsImage.checkIcon),
fit: BoxFit.cover,
),
)
: Container(
padding: EdgeInsets.only(top: 5, bottom: 5),
height: 40,
width: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: SizedBox(),
),
],
),
),
),
);
},
),
),
],
),
);
}
}
2nd Approach (Making changes to Parent Widget while staying on child Widget)
Make your Parent Widget's State Class and changeData function public (i.e. remove underscore before the state class name)
i.e.
class YourParentWidget extends StatefulWidget {
YourParentWidget({Key key}) : super(key: key);
#override
YourParentWidgetState createState() => YourParentWidgetState();
}
class YourParentWidgetState extends State<YourParentWidget> {
String _areaId;
int _coastPerPerson;
int _selectedBranchChecked;
String _formatted;
bool _showCreateReservationButton;
bool _isExpanded;
int _expandedIndex;
void changeData({
String areaId,
int coastPerPerson,
int selectedBranchChecked,
String formatted,
bool showCreateReservationButton,
bool isExpanded,
int expandedIndex
}){
setState(() {
_areaId = areaId;
_coastPerPerson = coastPerPerson;
_selectedBranchChecked = selectedBranchChecked;
_formatted = formatted;
_showCreateReservationButton = showCreateReservationButton;
_isExpanded = isExpanded;
_expandedIndex = expandedIndex;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
///Your UI Conponents....
);
}
}
Then Pass widget.key fro YourParentWidget to yor SelectBranchWidget (If you'reusing it inside your UI of YourParentWidget) i.e.
return Scaffold(
body: Column(
children: [
///Your UI Conponents....
SelectBranchWidget(parentKey: widget.key,
///... Other PArameters as well
)
],
)
);
Now in Your SelectBranchWidget Widget, do the following
class SelectBranchWidget extends StatefulWidget {
GlobalKey<YourParentWidgetState> parentKey;
List<RestaurantBranch> branches;
int coastPerPerson;
String areaId;
int selectedBranchChecked;
String formatted;
bool isExpanded;
bool showCreateReservationButton;
int expandedIndex;
Function changeData;
SelectBranchWidget(
{
this.parentKey,
this.branches,
this.coastPerPerson,
this.areaId,
this.selectedBranchChecked,
this.formatted,
this.isExpanded,
this.showCreateReservationButton,
this.expandedIndex,
this.changeData,
});
#override
_SelectBranchWidgetState createState() => _SelectBranchWidgetState();
}
class _SelectBranchWidgetState extends State<SelectBranchWidget> {
void _changedValues(int i, int branchAreaIndex) {
widget.coastPerPerson = widget.branches[i].branchAreas[branchAreaIndex].costPerSeat;
widget.areaId = widget.branches[i].branchAreas[branchAreaIndex].guid;
print('areaId IS ${widget.areaId}');
widget.selectedBranchChecked = i;
widget.formatted = null;
widget.isExpanded = false;
widget.showCreateReservationButton = false;
widget.expandedIndex = -1;
///Then do this to make changes to your parent widget's state
widget.parentKey.currentState.changeData(
areaId: widget.areaId,
coastPerPerson: widget.coastPerPerson,
selectedBrnachChecked:widget.selectedBranchChecked,
formatted: widget.formatted,
showCreateReservationButton:widget.showCreateReservationButton,
isExpanded:widget.isExpanded,
expandedIndex: widget.expandedIndex
);
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(Radius.circular(10))),
padding: EdgeInsets.all(8),
margin: EdgeInsets.only(left: 16, right: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
'Select Branch',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
),
SizedBox(
height: 8,
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(
'Number of branches = ${widget.branches.length}',
style: TextStyle(color: Colors.grey, fontSize: 10),
),
),
),
SizedBox(
height: 16,
),
ConstrainedBox(
constraints:
BoxConstraints(maxHeight: 230, maxWidth: double.infinity),
child: ListView.builder(
shrinkWrap: true,
itemCount: widget.branches.length,
itemBuilder: (ctx, i) {
return GestureDetector(
onTap: () {
if (widget.branches[i].branchAreas.length == 1) {
_changedValues(i, 0);
return;
}
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).backgroundColor,
title: Text(
'Select Area',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
content: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
// height: (_branches[i].branchAreas.length == 1)
// ?70
// : (_branches[i].branchAreas.length == 2)
// ? 100
// :150,
width: 100.0,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: 120),
child: ListView.builder(
shrinkWrap: true,
itemCount:
widget.branches[i].branchAreas.length,
itemBuilder:
(BuildContext context, int index) {
return GestureDetector(
onTap: () {
_changedValues(i, index);
Navigator.of(context).pop();
},
child: Padding(
padding: const EdgeInsets.only(
top: 16, left: 36.0, right: 36),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
widget.branches[i]
.branchAreas[index].name,
style: TextStyle(
color: Colors.white),
),
Divider(
color: Colors.grey,
thickness: 1,
),
],
),
),
);
},
),
),
),
);
});
},
child: Card(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
color: Theme.of(context).backgroundColor,
child: Container(
padding: EdgeInsets.all(20),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.branches[i].branchDistrict.name,
style: TextStyle(color: Colors.white, fontSize: 16),
),
(widget.selectedBranchChecked == i)
? Container(
height: 40,
width: 40,
child: Image.asset(
getAssetsName(AssetsImage.checkIcon),
fit: BoxFit.cover,
),
)
: Container(
padding: EdgeInsets.only(top: 5, bottom: 5),
height: 40,
width: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: SizedBox(),
),
],
),
),
),
);
},
),
),
],
),
);
}
}
Edit: You just need to call (wherever you're initiating it from) your YourParentWidget like YourParentWidget(key: GlobalKey<YourParentWidgetState>())
Pardon me if any typo occurs

Flutter - How to add scrollview to column with listview in flutter

I have a container with a column that have three containers and one listview wrapped with expanded which works perfectly but am trying to add a scrollview to the whole container so that on the three scroll with the listview, please how can I achieve this? Below is what my code looks like.
List<Content> content = [];
_fetchComments() async {
setState(() {
isLoading = true;
_isDealButtonRefresh = true;
});
try {
final result = await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
print('connected');
String baseURL;
debugPrint("my select:$_mySelection");
if (_mySelection == null && _myFeatureSelection == null) {
baseURL = "myjson link";
} else if (_myFeatureSelection != null) {
baseURL =
"my json link" +
_myFeatureSelection;
debugPrint("feature enter");
_mySelection = null;
} else if (_mySelection != null && _myFeatureSelection == null) {
baseURL = "my json link" +
_mySelection;
}
print("our url:$baseURL");
final response = await http.get(baseURL);
if (response.statusCode == 200) {
print(response.body);
content = (json.decode(response.body) as List)
.map((data) => new Content.fromJson(data))
.toList();
setState(() {
print(response.body);
isLoading = false;
_isDealButtonRefresh = false;
});
} else {
print(response.body);
throw Exception('Failed to load post');
}
}
} on SocketException catch (_) {
print('not connected');
setState(() => isLoading = false);
Navigator.pushReplacement(
context,
new MaterialPageRoute(
builder: (BuildContext context) => NoInternet()));
}
}
initState() {
super.initState();
_fetchComments();
}
body: Container(
child: Column(children: <Widget>[
Container(),
Container(),
Container(),
Expanded(child: ListView.separated(
separatorBuilder:
(context, index) => Divider(
color: Colors.grey,
),
itemCount: content == null
? 0
: _searchResult.length,
itemBuilder:
(context, position) {
final current =
_searchResult[position];
double myrate = double.parse(
_searchResult[position]
.ratings ==
null
? "0"
: _searchResult[position]
.ratings);
debugPrint("rato:$myrate");
return FutureBuilder<String>(
future: getDistance(
current.lat,
current.lng)
.then((value) =>
value.toString()),
builder:
(context, snapshot) {
return Container(
child:
GestureDetector(
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext ctx) => Maps(_searchResult[position])));
},
child:
Column(
children: <
Widget>[
Row(
children: <
Widget>[
Expanded(
child:Container(
height:150,
width: MediaQuery.of(context).size.width,
child: SizedBox(
child: FadeInImage(image: NetworkImage(_searchResult[position].thumbnail_name), placeholder: AssetImage("assets/640x360.png"),
fit: BoxFit.cover,),
),
)),
],
),
Container(
padding:
const EdgeInsets.all(5.0),
child:
Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.only(bottom: 1.0),
child: Text(
_searchResult[position].title,
style: TextStyle(fontFamily: 'Montserrat', fontSize: 13, fontWeight: FontWeight.bold),
),
),
Container(
padding: const EdgeInsets.only(bottom: 1.0),
child: Text(
_searchResult[position].address,
maxLines: 2,
style: TextStyle(fontFamily: 'Montserrat', fontSize: 10, color: Colors.black54),
),
),
Container(
padding: const EdgeInsets.only(bottom: 1.0),
child: _status != PermissionStatus.denied
? snapshot.hasData
? Text(
snapshot.data + " " + "km",
maxLines: 1,
style: TextStyle(fontFamily: 'Montserrat', fontSize: 10, fontWeight: FontWeight.bold, color: colorBlue),
)
: SizedBox(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(
colorBlue,
),
strokeWidth: 1,
),
height: 5.0,
width: 5.0,
)
: Icon(
Icons.do_not_disturb,
color: Colors.red,
size: 15,
), ),
SizedBox(
height: 2,
),
Container(
child: Text(
_searchResult[position].price + " " + "USD",
style: TextStyle(
color: colorPink,
fontWeight: FontWeight.bold,
fontSize: 12,
fontFamily: 'Montserrat',
),
),
)
],
crossAxisAlignment: CrossAxisAlignment.start,
),
flex: 9,
),
Column(
children: <Widget>[
Container(
padding: const EdgeInsets.only(bottom: 0),
child: SmoothStarRating(
allowHalfRating: false,
onRatingChanged: (v) {
setState(() {});
},
starCount: 5,
rating: myrate,
size: 12.0,
filledIconData: Icons.star,
halfFilledIconData: Icons.star_half,
color: Colors.orange,
borderColor: Colors.orange,
spacing: 0.0)),
Text(
"(" + myrate.toStringAsFixed(1) + ")",
style: TextStyle(color: Colors.black, fontFamily: 'Montserrat', fontSize: 10, fontWeight: FontWeight.bold, fontStyle: FontStyle.normal),
),
],
)
],
),
)
],
)));
});
}))
])
)
In the above code the listview fetch it data from json rest API.
You could add the 3 inner containers as children of the listview.
Something like this:
Widget getListView(yourListWithData) {
List<Widget> listViewChildren = [
Container(),
Container(),
Container(),
];
listViewChildren.addAll(
yourListWithData
.map(
(e) => Text(e), //text widget as an example, use your own widget
)
.toList(),
);
return ListView(
children: listViewChildren,
);
}
And then you can get rid of the Column and make the listview the only child of the parent container:
Container(
child: getListView(yourListWithData),
);
To solve that:
Add SingleChildScrollView Widget at the beginning with physics: ClampingScrollPhysics() propertie .
Add shrinkWrap: true propertie to your Listview .
SingleChildScrollView(
scrollDirection: Axis.vertical,
physics: ClampingScrollPhysics(),
child: Container(
child: Column( crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(),
Container(),
Container(),
Expanded(child: Listview.builder(shrinkWrap: true,))
])
)
I hope to help you
So finally after many months of research, I was able to resolve this by giving the height of each container. Since I want the height of the listview to cover most of the space in the activity I didn't set the height for the Container with the ListView. First I changed the Expanded() above to Container(), set ListView shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), . Find below my updated code:
body: Container(
child: Column(children: <Widget>[
Container(height: MediaQuery.of(context).size.height * 0.16,
),
Container(height: MediaQuery.of(context).size.height * 0.16,
),
Container(height: MediaQuery.of(context).size.height * 0.16,
),
Container(child: ListView.separated(shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder:
(context, index) => Divider(
color: Colors.grey,
),
itemCount: content == null
? 0
: _searchResult.length,
itemBuilder:
(context, position) {
final current =
_searchResult[position];
double myrate = double.parse(
_searchResult[position]
.ratings ==
null
? "0"
: _searchResult[position]
.ratings);
debugPrint("rato:$myrate");
return FutureBuilder<String>(
future: getDistance(
current.lat,
current.lng)
.then((value) =>
value.toString()),
builder:
(context, snapshot) {
return Container(
child:
GestureDetector(
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext ctx) => Maps(_searchResult[position])));
},
child:
Column(
children: <
Widget>[
Row(
children: <
Widget>[
Expanded(
child:Container(
height:150,
width: MediaQuery.of(context).size.width,
child: SizedBox(
child: FadeInImage(image: NetworkImage(_searchResult[position].thumbnail_name), placeholder: AssetImage("assets/640x360.png"),
fit: BoxFit.cover,),
),
)),
],
),
Container(
padding:
const EdgeInsets.all(5.0),
child:
Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.only(bottom: 1.0),
child: Text(
_searchResult[position].title,
style: TextStyle(fontFamily: 'Montserrat', fontSize: 13, fontWeight: FontWeight.bold),
),
),
Container(
padding: const EdgeInsets.only(bottom: 1.0),
child: Text(
_searchResult[position].address,
maxLines: 2,
style: TextStyle(fontFamily: 'Montserrat', fontSize: 10, color: Colors.black54),
),
),
Container(
padding: const EdgeInsets.only(bottom: 1.0),
child: _status != PermissionStatus.denied
? snapshot.hasData
? Text(
snapshot.data + " " + "km",
maxLines: 1,
style: TextStyle(fontFamily: 'Montserrat', fontSize: 10, fontWeight: FontWeight.bold, color: colorBlue),
)
: SizedBox(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(
colorBlue,
),
strokeWidth: 1,
),
height: 5.0,
width: 5.0,
)
: Icon(
Icons.do_not_disturb,
color: Colors.red,
size: 15,
), ),
SizedBox(
height: 2,
),
Container(
child: Text(
_searchResult[position].price + " " + "USD",
style: TextStyle(
color: colorPink,
fontWeight: FontWeight.bold,
fontSize: 12,
fontFamily: 'Montserrat',
),
),
)
],
crossAxisAlignment: CrossAxisAlignment.start,
),
flex: 9,
),
Column(
children: <Widget>[
Container(
padding: const EdgeInsets.only(bottom: 0),
child: SmoothStarRating(
allowHalfRating: false,
onRatingChanged: (v) {
setState(() {});
},
starCount: 5,
rating: myrate,
size: 12.0,
filledIconData: Icons.star,
halfFilledIconData: Icons.star_half,
color: Colors.orange,
borderColor: Colors.orange,
spacing: 0.0)),
Text(
"(" + myrate.toStringAsFixed(1) + ")",
style: TextStyle(color: Colors.black, fontFamily: 'Montserrat', fontSize: 10, fontWeight: FontWeight.bold, fontStyle: FontStyle.normal),
),
],
)
],
),
)
],
)));
});
}))
])
)