I have created a string List and applied a checkbox and when the checkbox clicked, the string list will be shown on the next screen but I am getting a range error. please help.
var _suggestions = <String>['this is me1','this is me2','this is me3' ];
final _saved = <String>['this is me1','this is me2','this is me3' ];
final _biggerFont = TextStyle(fontSize: 18.0);
this is the string that I have defined.
void _pushSaved(){
Navigator.of(context).push(
MaterialPageRoute<void>(
// NEW lines from here...
builder: (BuildContext context) {
final tiles = _saved.map(
(String pair) {
return ListTile(
title: Text(
pair,
style: _biggerFont,
),
);
},
);
final divided = ListTile.divideTiles(
context: context,
tiles: tiles,
).toList();
return Scaffold(
appBar: AppBar(
title: Text('Saved Suggestions'),
),
body: ListView(children: divided),
);
}, // ...to here.
),
);
}
this is some page route
Widget _buildSuggestions() {
return ListView.builder(
padding: EdgeInsets.all(16.0),
itemBuilder: /*1*/ (context, i) {
if (i.isOdd) return Divider(); /*2*/
final index = i ~/ 2; /*3*/
_suggestions = <String>['this is me1','this is me2','this is me3'];
return _buildRow(_suggestions[index]);
});
}
There is no problem in showing selected checkbox data into the next screen but I don't know why the range error is showing.
Widget _buildRow(String pair) {
final alreadySaved = _saved.contains(pair);
return Container(
decoration: new BoxDecoration (
color: HexColor('#F2FFFF'),
border: Border.all(color: HexColor('#09B9B6')),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: ListTile(
title: Text(
pair,
style: _biggerFont,
),
trailing: Icon(
alreadySaved ? Icons.check_box : Icons.check_box_outline_blank_outlined,
color: alreadySaved ? HexColor('#09B9B6') :null,
),
onTap: (){
setState(() {
if (alreadySaved){
_saved.remove(pair);
}
else{
_saved.add(pair);
}
});
},
),
);
}
this is the build row function
We can also use ListView.separated
ListView.separated(
padding: EdgeInsets.all(16.0),
itemBuilder: (context, index) {
return _buildRow(_suggestions[index]);
},
separatorBuilder: (_, __) => Divider(),
itemCount: _suggestions.length);
Related
I have a variable to which i assign a value inside gridView.Builder and there is a button, when clicked on which my variable should change, I use setState for this, but it does not change, what could be the reason?
class _CatalogItemsState extends State<CatalogItems> {
Set<int> _isFavLoading = {};
bool isFavorite = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(name!),
),
body: Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Column(
children: [
Expanded(
child: FutureBuilder<List<Product>>(
future: productFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return buildGridShimmer();
} else if (snapshot.hasData) {
final catalog = snapshot.data;
if (catalog!.isEmpty) {
return const Center(
child: Text(
'Нет товаров',
style: TextStyle(
fontSize: 25, fontWeight: FontWeight.bold),
),
);
}
return buildCatalog(catalog);
} else {
print(snapshot.error);
return const Text("No widget to build");
}
}),
),
],
),
),
);
}
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(),
delegate: SliverChildBuilderDelegate(childCount: product.length,
(BuildContext context, int index) {
final media =
product[index].media?.map((e) => e.toJson()).toList();
final photo = media?[0]['links']['local']['thumbnails']['350'];
final productItem = product[index];
isFavorite = productItem.is_favorite; // this is the new value of the variable, work good
IconButton(
icon: Icon(_isFavLoading.contains(index) || isFavorite ? Icons.favorite : Icons.favorite_border, color: Colors.red,),
onPressed: () {
setState(() {
isFavorite = !isFavorite;
});
print('t: ${isFavorite}'); // the value of the variable does not change
},
)
This is because you're not really updating your product object. You must change its value when icon is pressed
onPressed: () {
setState(() {
productItem.is_favorite = !isFavorite;
});
print('t: ${productItem.is_favorite}'); // the value of the variable does not change
}
Hi the listview that is displayed below allows you to scroll a series of elements that are shown in a flutter list, the problem is that it is not possible to scroll the entire list, how can I correct this thing?
Flutter code:
import 'package:costal/Model/Maintenance.dart';
import 'package:costal/View/constants/color.dart';
import 'package:costal/View/utils/support.dart';
import 'package:costal/View/widgets/SceduledCard.dart';
import 'package:flutter/material.dart';
import 'package:rflutter_alert/rflutter_alert.dart';
import 'DetailScreen.dart';
class ScheduledScreen extends StatelessWidget {
const ScheduledScreen({Key? key}) : super(key: key);
Future<List<Maintenance>> loadvalues() async {
return await Maintenance.getMaintenanceScheduled();
}
Future<bool> completeDay(BuildContext context, Maintenance man) async {
var check = await man.completeDay();
if (check == true) {
Share.alertCustom(context, AlertType.success, 'Manutenzione Completa', 'Hai completato la manutenzione', true);
} else {
Share.alertCustom(context, AlertType.error, 'Errore', 'non è stato possibile completare le manutenzioni', false);
}
return check;
}
Widget getLoader() {
return const Align(
alignment: Alignment.center,
child: CircularProgressIndicator(
value: 50,
semanticsLabel: 'Loading value',
),
);
}
#override
Widget build(BuildContext context) {
return FutureBuilder<List<Maintenance>>(
future: loadvalues(),
builder: (BuildContext context, AsyncSnapshot<List<Maintenance>> snapshot) {
if (!snapshot.hasData) {
return getLoader();
} else {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text(
'Manutenzioni',
style: TextStyle(
fontFamily: 'Poppins',
color: kPrimaryColor,
),
),
leading: CircleAvatar(
radius: 20,
backgroundColor: const Color(0xff94d500),
child: IconButton(
icon: const Icon(Icons.access_alarms_sharp),
onPressed: () async {
completeDay(context, snapshot.data![0]);
},
),
),
),
body: Wrap(
//the magic
children: [
Container(
padding: const EdgeInsets.all(20),
child: ListView.separated(
shrinkWrap: true,
itemCount: snapshot.data!.length,
separatorBuilder: (context, index) {
return const SizedBox(
height: 10,
);
},
itemBuilder: (context, index) {
return GestureDetector(
onTap: (() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailScreen(snapshot.data![index])),
);
}),
child: SceduledCard(man: snapshot.data![index], c: Colors.blue),
);
}))
],
),
);
}
});
}
}
I'm building a chatapp which displays all the available chats as ChatTiles.
A ChatTile shows the name and the last message from the chat using a Stream from Firebase.
By clicking on a ChatTile the Navigator pushes a ConversationScreen Widget.
I would like to somehow pass the stream data along the widget tree to the ConversationScreen. So whenever a message is inserted, the ChatTile shows the last message and the ConversationScreen as well.
But my error is this:
Stream has already been listened to.
Here's a picture:
ChatTiles
ChatTile:
class _ChatRoomTileState extends State<ChatRoomTile> {
Stream<QuerySnapshot> chatRoomStream;
DataFromMessages dataFromMessages;
List<Message> messages;
#override
void initState() {
chatRoomStream =
DB_Service.streamChatRooms(context, widget.dataFromChatRoom.chatRoomId);
getUser();
super.initState();
}
#override
Widget build(BuildContext context) {
if (chatRoomStream == null) return Container();
return StreamProvider<QuerySnapshot>.value(
value: chatRoomStream,
initialData: null,
builder: (context, f) {
QuerySnapshot snapshot = Provider.of<QuerySnapshot>(context);
if (snapshot == null) return Container();
dataFromMessages =
dataFromMessagesFromJson(json.encode(snapshot.docs.first.data()));
messages = dataFromMessages.messages.entries
.map((e) => e.value)
.toList()
.reversed
.toList();
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ConversationScreen(
dataFromChatRoom: widget.dataFromChatRoom,
uid: widget.uid,
chatRoomStream: chatRoomStream,
),
),
);
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 0.5),
borderRadius: BorderRadius.circular(8),
),
child: Row(
...
),
),
);
},
);
}
}
The pushed ConversationScreen:
class _ConversationScreenState extends State<ConversationScreen> {
TextEditingController messageController = TextEditingController();
DataFromMessages dataFromMessages;
List<Message> messages;
#override
Widget build(BuildContext context) {
print("Building ConversationScreen");
if (widget.chatRoomStream == null) return Container();
return StreamBuilder(
stream: widget.chatRoomStream.asBroadcastStream(),
initialData: null,
builder: (context, snap) {
//QuerySnapshot snap = Provider.of<QuerySnapshot>(context);
if (snap == null) return Text(" empty");
dataFromMessages = dataFromMessagesFromJson(
json.encode(snap.data.docs.first.data()));
messages = dataFromMessages.messages.entries
.map((e) => e.value)
.toList()
.reversed
.toList();
return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold(
appBar: buildAppBar(context),
body: Column(
children: [
Expanded(
child: messages != null
? ListView.builder(
shrinkWrap: true,
itemCount: messages.length,
itemBuilder: (context, index) {
return ChatTile(
data:
messages[index].data == widget.uid ? 1 : 0,
message: messages[index].message,
timestamp: messages[index].timestamp,
bubbleNip: BubbleNip.no,
);
})
: Container(),
),
TextInput(
uid: widget.uid,
chatRoomId: widget.dataFromChatRoom.chatRoomId,
messages: messages,
),
],
),
bottomNavigationBar: const SizedBox(height: 50),
),
);
});
}
AppBar buildAppBar(BuildContext context) {
return AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
title: Text(
"Chat with Somebody",
style: TextStyle(
color: Theme.of(context).indicatorColor,
),
),
leading: BackButton(color: Theme.of(context).indicatorColor),
);
}
}
I have US states displayed on the screen. They are displayed using a ListView. I need to make it so that when you click on one of the states, a checkmark appears. Now in the trailing I added an icon, but when you click on one state, a checkmark appears on all. How can this be implemented?
class _AddStatePageState extends State<AddStatePage> {
static const List<String> _usaStates = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
...
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: const AppBarWithSearch(
appBarTitle: 'Add State',
),
body: Padding(
padding: const EdgeInsets.only(top: 24),
child: ListView.separated(
itemCount: _usaStates.length,
itemBuilder: (context, index) {
return ListTile(
trailing: Image.asset(
Assets.assetsCheckmark,
width: 13,
height: 10,
),
title: Text(
_usaStates[index],
),
);
},
separatorBuilder: (context, index) {
return const Divider();
},
),
),
);
}
}
Something along these lines:
class _AddStatePageState extends State<AddStatePage> {
static const List<String> _usaStates = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
...
];
static const List<bool> _usaStatesSelected = [false, false, true, ...];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: const AppBarWithSearch(
appBarTitle: 'Add State',
),
body: Padding(
padding: const EdgeInsets.only(top: 24),
child: ListView.separated(
itemCount: _usaStates.length,
itemBuilder: (context, index) {
return ListTile(
onTap: () {
setState(() {
for(var i = 0; i < _usaStatesSelected.length; i++) {
_usaStatesSelected[i] = false;
}
_usaStatesSelected[index] = true;
});
},
trailing:
_usaStatesSelected[index] == false
? SizedBox.shrink()
: Image.asset(
Assets.assetsCheckmark,
width: 13,
height: 10,
),
title: Text(
_usaStates[index],
),
);
},
separatorBuilder: (context, index) {
return const Divider();
},
),
),
);
}
}
ListTile provide onTap method, you can use it. To show selected item, create a variable that will holds the selected index on state class.
int? _selectedIndex;
and ListTile will be
return ListTile(
onTap: () {
_selectedIndex=index;
setState(() {});
},
trailing:
_selectedIndex==index ? Icon(Icons.check) : null,
Replace Icon(Icons.check) with your image.
I got a page(SearchComparePage) that has a few listTiles with checkboxes. When the FloatingActionButton is pressed, it sends the "mockIdList" which is a list that gets things added to it when the checkboxes are checked. So my problem is that when I press the button and send the list to the other page and I move back to my SearchComparePage, then the mockIdList is still filled with the data from the checked boxes from earlier. Is it possible to somehow clear the mockIdList everytime the page is loaded?
Im sorry if my question is weird. I'm not good at asking questions.
class SearchComparePage extends StatefulWidget{
SearchComparePage({Key key, this.title}) : super(key: key);
final String title;
_SearchState createState()=> _SearchState();
}
class _SearchState extends State<SearchComparePage> {
List<String> mockIdList = [];
String searchString;
String name = "";
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
//backgroundColor: Color(0xFF8BC34A),
appBar: AppBar(
leading: IconButton(
onPressed: () {
Navigator.of(context).pop();
},
icon: Icon(Icons.arrow_back_ios),
color: Colors.white,
),
backgroundColor: Colors.green,
//elevation: 0,
title: Text("Enklere valg"),
),
body: Column(
children: <Widget> [
Expanded(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
onChanged: (value) {
setState(() {
searchString = value.toLowerCase();
});
},
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
contentPadding: EdgeInsets.only(left: 25.0),
hintText: 'Søk etter produkt',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4.0)
)
),
),
),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
),
child: StreamBuilder<QuerySnapshot>(
stream:
(searchString == null || searchString.trim() == "")
? Firestore.instance
.collection('products')
.snapshots()
: Firestore.instance
.collection("products")
.where("searchIndex",
arrayContains: searchString)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
return new ListView(
children: snapshot.data.documents
.map((DocumentSnapshot document) {
return buildResultCard(context, document);
}).toList(),
);
}
}
)
)
)
],
),
)
],
),
// Button that sends the information in mockIdList to the other page.
floatingActionButton: FloatingActionButton(child: Icon(Icons.add),onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => CompareResultPage(idList: mockIdList,)),
);
},
),
),
);
}
// Widget that builds the listtiles
Widget buildResultCard(context, data) {
bool _isChecked = false;
navigateToDetail(context, data) {
Navigator.push(context, MaterialPageRoute(builder: (context) => ProductPage(product_id: data['product_id'],)));
}
return StatefulBuilder(
builder: (context, StateSetter setState) {
return Center(
child: ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(data['product_image'] ?? ''),
),
title: Text(data['product_maker'] ?? ''),
subtitle: Text(data['product_name'] ?? ''),
trailing: Checkbox(
value: _isChecked,
onChanged: (bool value) {
if (value == true) {
setState(() {
mockIdList.add(data['product_id']);
_isChecked = value;
});
}
if (value == false) {
setState(() {
mockIdList.remove(data['product_id']);
_isChecked = value;
});
}
},
),
onTap: () => navigateToDetail(context, data),
),
);
}
);
}
}
When you return from your other view you'll want to reset your mockIdList
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductPage(
product_id: data['product_id'],
),
),
).then((_) => setState(() => mockIdList.clear())); // <----Add this line of code