I have this document :
for (var i = 0; i < ligneDocuments.length; i++) {
if (ligneDocuments[i]['id_doc'] == widget.ligneDocumentId) {
setState(() {
TextEditingController idController = new TextEditingController();
widget.controllers.add(idController);
idController.text = ligneDocuments[i]['id'].toString();
TextEditingController refController = new TextEditingController();
widget.controllers.add(refController);
refController.text = ligneDocuments[i]['refProd'].toString();
TextEditingController nomController = new TextEditingController();
widget.controllers.add(nomController);
nomController.text = ligneDocuments[i]['nomProd'].toString();
TextEditingController qteController = new TextEditingController();
widget.controllers.add(qteController);
qteController.text = ligneDocuments[i]['qteProd'].toString();
TextEditingController prixController = new TextEditingController();
widget.controllers.add(prixController);
prixController.text = ligneDocuments[i]['prixProd'].toString();
TextEditingController totalProdController =
new TextEditingController();
totalProdController.text = (double.parse(prixController.text) *
double.parse(qteController.text))
.toString();
print(kk);
kk++;
print(kk);
_cardList.add(InputRefNomProduit(
index:kk,
totalDoc: totalDoc,
totalDocument: totalDocument,
total: total,
controllers: widget.controllers,
label: 'Référence',
label2: 'Nom du produit',
label3: 'Quantité',
label4: 'Prix',
label5: 'Total par Produit',
fieldController: refController,
fieldController2: nomController,
fieldController3: qteController,
fieldController4: prixController,
fieldController5: totalProdController,
delete: (index){
print(index);
setState(() {
_cardList.removeAt(index);
});
},
));
});
}
This is how I'm printing my _cardList :
SizedBox(
height: 450,
width: 1200,
child: ListView.builder(
itemCount: _cardList.length,
itemBuilder: (context, index) {
return Padding(padding:
EdgeInsets.symmetric(vertical: 5),child: _cardList[index],);
},
),
),
class _InputRefNomProduitState extends State<InputRefNomProduit> {
bool hasFocus = false;
String nomProduit;
String selectedProduit;
int produitId;
List produits = [];
List<String> refProduits = [];
#override
void initState() {
super.initState();
this.fetchProduits();
}
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Row(
children: <Widget>[
Expanded(
flex: 2,
child: Container(
child: Text(
"${widget.label}",
textAlign: TextAlign.left,
style: TextStyle(
fontWeight: FontWeight.w900,
color: Color.fromARGB(255, 255, 255, 255),
),
),
),
),
// more Expanded inputs
IconButton(onPressed: (){
widget.delete(widget.index);
}, icon: Icon(Icons.close)),
],
);
},
);
}
}
at the top , I'm trying to check if some inputs are == to what exists in my BD .
Then , print the items with a Icon Button with remove function .
I'm facing the problem that when I delete an item , I'm getting different counter for the _cardList length , and the index of item that I want to delete.
After deleting 1 item, I'm getting error like this :
Another exception was thrown: RangeError: Value not in range: 3.
The card list length went from 4 to 3 and the item I'm trying to delete is still index=3.
I tried without the index[kk] variable using just removeAt[index] , and it didn't work.
these are one of two options you can try.
1.
_cardList.removeAt(index);
setState(() {
});
//outside of loop
void refresh(){
setState((){});
}
//then inside loop
_cardList.removeAt(index);
refresh();
The 1. answer is how I solved my issue which is dervied from this stackoverflow post.
Flutter , index out of range when i try to delete an item from a list
Related
I am calling rest api and showing the list items. I want to implement "Show More " Button after 10 list items, if the button is clicked complete list of items should be shown to the users. And after the complete list of items i.e at the bottom or end "Show Less" button should be shown when clicked on it, it should go to the top i.e the start of list items.
My code is as follows
List<WalletHistoryResponseResult?> ? result =[];
late final TextEditingController _controller;
int present = 0;
int perPage = 10;
ScrollController _scrollController = new ScrollController();
bool isPerformingRequest = false;
int pageNumber = 0;
final originalItems = List<String>.generate(35, (index) => "WalletHistoryItem(walletHistory: state.walletHistory!.result![index]!)");
List<String> items = [];
#override
void initState() {
super.initState();
_controller = TextEditingController();
setState(() {
items.addAll(originalItems.getRange(present, present + perPage));
present = present + perPage;
});
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
void loadMore() {
setState(() {
if((present + perPage )> originalItems.length) {
items.addAll(
originalItems.getRange(present, originalItems.length));
} else {
items.addAll(
originalItems.getRange(present, present + perPage));
}
present = present + perPage;
});
}`
```
```
`
```
ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (context, index) => const SizedBox(height: 16),
itemBuilder: (context, index) {
if(index == (items.length) ) {
index!=null;
return
Container(
width: 90,
height: 80,
color: AppColors.colorF5F5F5,
child: Container(
width: 87,
height: 22,
child: TextButton(
child: Text("Show More",style:TextStyle(
decoration: TextDecoration.underline,
color: AppColors.color2F74AC,fontSize: 16, fontWeight: FontWeight.w600),),
onPressed: () {
loadMore();
},
),
),
);
}
return state.walletHistory!.result?[index]!=null ? WalletHistoryItem(walletHistory: state.walletHistory!.result![index]!):Center(
child: SizedBox(
height: 16,
width: 16,
child : CircularProgressIndicator(
),
),
);
},
itemCount: (present <= originalItems.length) ? items.length + 1 : items.length,
),
Please explain and help me fix my query
I have defaultly given the number of list items count as 35 , but it should get the item count by itself and show the "Show Less" button after the end of list items
final originalItems = List<String>.generate(35, (index) => "WalletHistoryItem(walletHistory: state.walletHistory!.result![index]!)");
Implemented Show more button after every 10 items, but only one show more button after 10 items is required.
int present = 0; int perPage = 10;
My first Flutter project, which is a tricycle booking system, has just begun. Using the ListView widget, I wanted to display all of the active passengers that are saved in my Firebase Database. However, when I attempted to display it and place it in a List, all functions are working fine at first click. When you click the button to view the ListView a second time, all of the saved data are replicated. The list continues after my third click and grows by three. The image below illustrates what takes place when I repeatedly click on the ListView.
These are the blocks of code that are utilized for this functionality:
CODE for Functionality
retrieveOnlinePassengersInformation(List onlineNearestPassengersList) async
{
dList.clear();
DatabaseReference ref = FirebaseDatabase.instance.ref().child("passengers");
for(int i = 0; i<onlineNearestPassengersList.length; i++)
{
await ref.child(onlineNearestPassengersList[i].passengerId.toString())
.once()
.then((dataSnapshot)
{
var passengerKeyInfo = dataSnapshot.snapshot.value;
dList.add(passengerKeyInfo);
print("passengerKey Info: " + dList.toString());
});
}
}
CODE for the UI
body: ListView.builder(
itemCount: dList.length,
itemBuilder: (BuildContext context, int index)
{
return GestureDetector(
onTap: ()
{
setState(() {
chosenPassengerId = dList[index]["id"].toString();
});
Navigator.pop(context, "passengerChoosed");
},
child: Card(
color: Colors.grey,
elevation: 3,
shadowColor: Colors.green,
margin: EdgeInsets.all(8.0),
child: ListTile(
leading: Padding(
padding: const EdgeInsets.only(top: 2.0),
child: Icon(
Icons.account_circle_outlined,
size: 26.sp,
color: Color(0xFF777777),
),
),
title: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
children: [
Text(
dList[index]["first_name"] + " " + dList[index]["last_name"],
style: TextStyle(
fontFamily: "Montserrat",
fontSize: 18.sp,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
Icon(
Icons.verified_rounded,
color: Color(0xFF0CBC8B),
size: 22.sp,
),
],
),
],
),
),
),
);
},
),
Expected Result:
Actual Result AFTER CLICKING MANY TIMES:
Made a demo for you how to call function once on load
class CustomWidgetName extends StatefulWidget {
const CustomWidgetName({Key? key}) : super(key: key);
#override
State<CustomWidgetName> createState() => _CustomWidgetNameState();
}
class _CustomWidgetNameState extends State<CustomWidgetName> {
List? dList = [];
void myDataFunction() async {
// do your data fetch and add to dList
final newList = [];
setState(() {
dList = newList;
});
}
#override
void initState() {
super.initState();
myDataFunction(); // Call your async function here
}
#override
Widget build(BuildContext context) {
return Scaffold();
}
}
Try this solution.
Update SelectNearestActiveDriversScreen() like this:
class SelectNearestActiveDriversScreen extends StatefulWidget
{
DatabaseReference? referenceRideRequest;
final List list;
SelectNearestActiveDriversScreen({this.referenceRideRequest, required this.list});
#override
State<SelectNearestActiveDriversScreen> createState() => _SelectNearestActiveDriversScreenState();
}
In homepage.dart, declare List dList = [];, then change line 378 like this:
Navigator.push(context, MaterialPageRoute(builder: (c)=> SelectNearestActiveDriversScreen(list: dList)));
In SelectNearestActiveDriversScreen(), replace all dList with widget.list.
Finally, if you are using variables in a specific file declare them in that file(not in another file) or pass them in the constructor of the class / file / widget /screen you are calling.
If you would rather use global variables and state managers go for packages like GetX.
say I have a song lyric app and there is just one Scaffold with a Text widget that displays the entire lyric and the lyrics are written in the format
....
Chorus:
...
....
....
and I have a FAB, onClick of which I need the text to auto scroll to the text "Chorus:", this text is literally in every song, but when the verses are a about 4+, they usually go off screen, so, user usually has to manually scroll to the chorus again after each verse that's beyond the screen height, but I need this to be done automatically at the tap of a button
scroll up till the string "chorus" is in view, how would I do this in flutter
TEXT
const kTheAstronomers = '''1. Yeah, you woke up in London
At least that's what you said
I sat here scrollin'
And I saw you with your aunt
A demon on your left
An angel on your right
But you're hypocritical
Always political
Chorus:
Say you mean one thing
But you go ahead and lie
Oh, you lie-lie, lie-lie
And you say you're not the bad type
2. Oh, you posted on Twitter
Said you had other plans
But your mother, she called me
Said, "Come have lunch with the fam"
Guess you didn't tell her that
You should've called me back
I guess you changed your number or somethin\' '''
LYRIC SCREEN
#override
Widget build(BuildContext context) {
return Scaffold(
extendBody: true,
body: SafeArea(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10),
child: Text(
kTheAstronomers,
style: const TextStyle(
fontSize: 30,
fontFamily: 'Montserrat',
fontWeight: FontWeight.w600,
),
),
),
),
)
floatingActionButton: FAB(onPressed: autoScrollToChorus),
,
You can create a GlobalKey and use the currentContext to scroll to the Chorus part.
final _key = GlobalKey()
Inside the autoScrollToChorus method you can add:
final context = _key.currentContext!;
await Scrollable.ensureVisible(context)
I found a way.
I had to change the way I displayed the text, instead of using one text widget, I used a ListView builder to display two texts, but before that, in initState, when my page receives the text, I split the text into a list of two separate texts, one containing the first part and the other containing from the Chorus down, then I give this list to the listview builder (you could also just use a column and create two separate widgets and just pass the scroll key to the second text, knowing it's the second part of the text)
final GlobalKey _key = GlobalKey();
void _autoScrollToChorus() async {
BuildContext context = _key.currentContext!;
await Scrollable.ensureVisible(context);
}
late List<String> lyricList;
#override
initState() {
lyricList =
kTheAstronomers.split(RegExp("(?=chorus)", caseSensitive: false));
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ListView.builder(
itemCount: lyricList.length,
itemBuilder: (context, idx) {
return Text(
key: idx == 1 ? _key : null,
lyricList[idx],
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 30,
),
);
}),
),
floatingActionButton: lyricList.length > 1 ? FloatingActionButton(
onPressed: _autoScrollToChorus,
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Text("Chorus"),
),
) : null,
);
}
Thanks to #Priyaank I knew to use the key and scroll to a particular widget
a more advanced solution that makes it possible to hide the button when the chorus is in view USING THE SCROLLABLE_POSITIONED_LIST PACKAGE
final GlobalKey _key = GlobalKey();
final ItemScrollController _itemScrollController = ItemScrollController();
final ItemPositionsListener _itemListener = ItemPositionsListener.create();
late List<String> lyricList;
bool chorusIsVisible = true;
void _autoScrollToChorus() {
// BuildContext context = _key.currentContext!;
// await Scrollable.ensureVisible(context);
_itemScrollController.scrollTo(
index: 1,
duration: const Duration(milliseconds: 500),
alignment: 0.5
);
}
#override
initState() {
lyricList =
kTheAstronomers.split(RegExp("(?=chorus)", caseSensitive: false));
super.initState();
if(lyricList.length > 1) {
_itemListener.itemPositions.addListener(() {
chorusIsVisible = _itemListener.itemPositions.value.where((item) {
final isTopVisible = item.itemLeadingEdge >= 0;
return isTopVisible;
}
).map((item) => item.index).toList().contains(1);
setState(() {});
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ScrollablePositionedList.builder(
itemScrollController: _itemScrollController,
itemPositionsListener: _itemListener,
itemCount: lyricList.length,
itemBuilder: (context, idx) {
return Text(
lyricList[idx],
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 30,
),
);
}),
),
floatingActionButton: lyricList.length > 1 && !chorusIsVisible ? FloatingActionButton(
onPressed: _autoScrollToChorus,
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Text("Chorus"),
),
) : null,
);
}
}
I am trying to use my ValueListenableBuilder, which generates Yes/ No buttons, to determine whether or not a textformfield will be visible. I.e. user selects the "Yes" button and the state of the app changes to allow the hidden textformfield to be displayed. I am extremely puzzled as to how I can either use setState, NotifyListeners, or ChangeNotifier to accomplish this task.
I am trying to avoid using either a radio button or making buttons outside of the ValueListenableBuilder because my ValueListenableBuilder is designed to generate a lot of my other buttons and I was hoping to incorporate one more function into them. Thanks in advance!
ValueListenableBuilder
ValueListenableBuilder<Option>(
valueListenable: yesNo,
builder: (context, option, _) => MakeButtons(
num0: 0,
num1: 1,
makeButtonWidth: MediaQuery.of(context).size.width * 0.20,
selected: option,
onChanged: (newOption) =>
yesNo.option = newOption,
ifSelected: (newOption) {
setState(() {
yesNo.option = newOption;
yesNo;
});
},
),
),
Make Buttons
enum Option {
option0,
option1,
}
class MakeButtons extends StatefulWidget {
MakeButtons({
this.num0,
this.num1,
this.selected,
this.onChanged,
this.ifSelected,
this.makeButtonWidth,
});
final int num0;
final int num1;
final double makeButtonWidth;
final Option selected;
final Function ifSelected;
final ValueChanged<Option> onChanged;
#override
_MakeButtonsState createState() => _MakeButtonsState();
}
class _MakeButtonsState extends State<MakeButtons> {
List<Widget> makeButtons(int num0, int num1, List<Widget> children,
List<Color> colors, List<Function> onPresses) {
List<Widget> buttons = new List();
for (int i = num0; i < num1; i++) {
buttons.add(Container(
constraints: BoxConstraints(
minWidth: widget.makeButtonWidth,
),
child: RectButton(
buttonChild: children[i],
bgColor: colors[i],
onPress: onPresses[i]),
));
}
return buttons;
}
Option selectedOption;
#override
Widget build(BuildContext context) {
List<Widget> children = [
AutoSizeText(
'Yes',
textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.w600, color: Colors.white),
),
AutoSizeText(
'No',
textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.w600, color: Colors.white),
),
];
List<Color> colors = [
selectedOption == Option.option0
? kActiveButtonColor
: kInactiveButtonColor,
selectedOption == Option.option1
? kActiveButtonColor
: kInactiveButtonColor,
];
List<Function> onPresses = [
() {
setState(() {
selectedOption = Option.option0;
});
return widget.onChanged(Option.option0);
},
() {
setState(() {
selectedOption = Option.option1;
});
return widget.onChanged(Option.option1);
},
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children:
makeButtons(widget.num0, widget.num1, children, colors, onPresses),
);
}
}
Visibility
visible: yesNo.title == 'A' ||
yesNo == 'Yes',
child: InputRow(
myUnit: defaultUnit,
inputParameter: 'Units',
textField: unitController,
colour: kEmoryDBlue,
),
),
Can anyone please help to implement this feature of Gmail that shows the counter to number of emails hidden when the email list becomes large ? I want to implement this in row widget where instead of being scrollable extra elements count is shown when overflow occurs.Gmail shows +15 counter for hidden emails
I was Curious to give a try to achieve the same effect, as asked.
Just in case, If anyone want a start for writing a custom one, then below code may help.
Here is my Code, Feel free to give any suggestions,
(For Now delete button in chips is not working bcoz of some logic problem, I will make it work another day)
import 'package:flutter/material.dart';
class Demo3 extends StatefulWidget {
#override
_Demo3State createState() => _Demo3State();
}
class _Demo3State extends State<Demo3> {
String temp = "";
bool showChips = false;
List<Widget> chipsList = new List();
TextEditingController textEditingController = new TextEditingController();
final _focusNode = FocusNode();
int countChipsToDeleteLater = 0;
#override
void initState() {
super.initState();
_focusNode.addListener(() {
print("Has focus: ${_focusNode.hasFocus}");
if (!_focusNode.hasFocus) {
showChips = false;
setState(() {});
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: new Container(
height: 500,
child: new Center(
child: Container(
width: 300,
child: !showChips
? Row(
children: [
buildTextField(),
showNumberWidgetIfAny(),
],
)
: Center(
child: Wrap(
children: [
Wrap(
children: buildChips(),
),
buildTextField(),
],
),
),
),
),
),
),
);
}
buildChips() {
return chipsList;
}
buildTextField() {
return Container(
width: 200,
child: new TextField(
showCursor: true,
focusNode: _focusNode,
autofocus: true,
cursorColor: Colors.black,
style: new TextStyle(fontSize: 22.0, color: Colors.black),
controller: textEditingController,
// decoration: InputDecoration.collapsed(
// hintText: "",
// ),
onChanged: (value) {
if (value.contains(" ")) {
checkWhatToStoreInChips(value, countChipsToDeleteLater);
textEditingController.clear();
setState(() {
showChips = true;
});
countChipsToDeleteLater++;
}
},
),
);
}
checkWhatToStoreInChips(String val, int chipsIndex) {
temp = "";
for (int i = 0; i < val.length; i++) {
if (val[i] == " ") {
break;
}
temp = temp + val[i];
}
addToChips(temp, chipsIndex);
}
addToChips(String tmp, int chipsIndex) {
chipsList.add(Chip(
// onDeleted: () {
// if (chipsList.length == 0) {
// countChipsToDeleteLater = 0;
// }
// chipsList.removeAt(chipsIndex);
// print(chipsList.length);
// print(chipsIndex);
// setState(() {});
// },
avatar: CircleAvatar(
backgroundColor: Colors.grey.shade800,
child: Text(tmp[0]),
),
label: Text(temp),
));
}
showNumberWidgetIfAny() {
int len = chipsList.length;
if (len >= 1) {
return GestureDetector(
onTap: () {
showChips = true;
setState(() {});
},
child: new Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.blue,
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: new Text(
"${chipsList.length.toString()} ",
style: new TextStyle(color: Colors.white, fontSize: 22),
),
),
),
);
}
return Container();
}
}
How it works:
Write something in text field, then press space, showChips boolean will become true
onChanged will detect the space and will send the string to a function.
That function will extract the string before space and then will add the string to a chip,
Finally the chip will be added to a chipslist.
We will have a boolean variable to check if the textfield is in focus and when to show the textfield and numberwidget (a widget which will keep count of the total chips, same like you asked in your question) or when to show the chipslist and textfield wraped in a wrap widget.
You can play around by changing the decoration of textfield to collapsed, to it look like the same as gmail.
Check this package, if you want to use custom package for ease.
I was facing a similar issue. I found a way to implement the Overflow count text.
Sample image
You basically have to paint the overflow text, and get its width like below
final TextPainter textPainter = TextPainter(
text: TextSpan(text: text, style: style),
textDirection: TextDirection.ltr,
textScaleFactor: WidgetsBinding.instance.window.textScaleFactor,
)..layout();
var textSize = textPainter.size;
textSize.width;
Then subtract that from the width available. Lets call it x.
Then create a sum of width for each row item(using TextPainter.layout() method mentioned above), till its value is less than x.
This way you'll know how many items can be shown in the row.
I have created a Flutter library to help with this.