I'm new on Flutter and I want to add Widgets tests to my app. But I have some problems and questions about widget tests a little more complex than flutter documentation.
Indeed i have a Scaffold widget with this body :
body: StreamBuilder<List<UserData>> (
stream: DatabaseService().searchUser(searchValue),
builder: (context, snapshot) {
if (snapshot.hasData && searchValue.length >= 3){
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(
onTap: () {
Navigator.pushReplacementNamed(context, '/friends/search/details', arguments: snapshot.data[index]);
},
title: Text(snapshot.data[index].name),
);
}
);
}
return Center();
},
)
The first question is:
Is it necessary to test this StreamBuilder?
And if yes: How can I mock the stream and test these widgets ?
Is it necessary to test this StreamBuilder?
This is subjective. Generally if there is anything that you feel is critical to the functionality of your app, then its better to write a test for it to ensure it works/behaves as expected always.
How can I mock the stream and test these widgets ?
There are tons of resources online to achieve this. Maybe have a look at this?
Related
I'm learning Flutter. I have a ListView and I would like to make the list items clickable. My idea is that when the user clicks on an item, it will be directed to another screen. Each buttom should leads to different screen. I'm having trouble implementing it, I don't know what to use: gesture detector or ontap. What should I do? Should I use ListTile instead of ListView?list viewdata
If you're using a ListView.builder, you can use a ListTile to add an onTap.
ListView.builder(
itemBuilder: (_, i) {
return ListTile(
title: Text('$i'),
onTap: () {}, // Handle your onTap here.
);
},
)
well, you can use Inkwell. ListView.builder( itemBuilder: (_, i) { return InkWell( onTap: (){}, child: ///widget, ); }, ) link enter link description here
I want to build a List of data which gets created when the Widget build. My Question is, how good is the performance when the list is big (ListLength ~= 1000)? Will there be performance issues by building the Widget or is this solution good?
Here an example whats the idea is:
List<Map<String, Object>> get listWithValues {
return List.generate(myListLength, (index) {
return {'value': index, 'date': DateTime.now()};
});
}
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: listWithValues.length,
itemBuilder: (context, i) {
return ListTile(
title: Text(listWithValues[i]['value'].toString()),
I have kind of a form where I can add cards, each having 5 textfields and 2 switches. I would like to use a method to build the switch code (and the textfield code, but that is working). However, the switches refuse to show their intended state. I saw couple of similar questions. However, most were solved with a list view listing all switched/checkboxes next to one another (I have multiple cards with multiple textfields and multiple switches, each). This was close, but I don't really understand the answer (within the comments)
Actually some answers come up with the same (I guess more or less same because mine isn't working) code storing the switch state in a bool list. When debugging I can see that the values are correctly stored in the list. However, the changed value is not rendered upon state change.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
var descrTECs = <TextEditingController>[];
var fixedSCs = [true]; //storing the switch values
var cards = <Card>[]; // storing the list of cards with forms
SizedBox createTextField(String placeholderStr, double fieldWidth) {
var tFieldController = TextEditingController();
switch (placeholderStr) { //switching placeholder to assign text controller to correct controller list
case "Description":
descrTECs.add(tFieldController);
break;
}
return SizedBox(width: fieldWidth, height: 25,
child: CupertinoTextField(
placeholder: placeholderStr,
controller: tFieldController,
),
);
}
SizedBox createSwitch(int pos) {
return SizedBox(width: 50, height: 25,
child: CupertinoSwitch(
value: fixedSCs[pos],
onChanged: (value) {
setState(() => fixedSCs[pos] = value); // value is stored in fixedSCs but not rendered upon rebuild
},
)
);
}
Card createCard() {
return Card(
child: Row(children: <Widget>[
Text('#p${cards.length + 1}:'),
Column(
children: <Widget>[
createSwitch(cards.length),
createTextField("Description", 70.0),
],),
],),
);
}
#override
void initState() {
super.initState();
cards.add(createCard()); // first card created upon start
}
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder( // List Builder to show all cards
itemCount: cards.length,
itemBuilder: (BuildContext context, int index) {
return cards[index];
},
),
),
RaisedButton(
child: Text('add new'),
onPressed: () => setState(() {
fixedSCs.add(true); // bool element created to create next card
cards.add(createCard());} // create next card
),
),
],
),
),);
}
}
One thing I do not understand in general: Upon rebuild after a state change cards.length} should be my number of cards, let's say 3. And when it renders the 1st card, it passes the line Text("#p${cards.length + 1}"), so it should show #p3 and not #p1. What do I get wrong here?
I meanwhile got this working with quite some logic changes.
I put the switch builder into a stateless widget
class createSwitch extends StatelessWidget {
const createSwitch({
this.label, this.margin=const EdgeInsets.all(0.0), this.width, this.height, this.value, this.onChanged});
final String label; final EdgeInsets margin; final double width; final double height; final bool value;
final Function onChanged;
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: <Widget>[
Expanded(child: Text(label)),
CupertinoSwitch(
value: value,
onChanged: (bool newValue) {onChanged(newValue);},
),
],
),
),
} }
In the parent stateful controller I created a list to store the switches' state var parameterSCs = [true]; and each time I add a card I add a value whith clicking the button onPressed: () => setState(() {parameterSCs.add(true);}
I no longer store the cards widgets as a list. Instead, I build them directly in the code within a ListView.builder
ListView.builder(
itemCount: parameterSCs.length,
itemBuilder: (BuildContext context, int index) {
return Card( ...
In my real code I have 2 switches per card, so I always add 2 elements and the ListView count is then half of the parameterSCs' length.
I tried loads of approaches, this was the only one working
I have a stateful widget where there is a list like this,
import 'package:flutter/material.dart';
import 'package:todoapp/widgets/tasks_tile.dart';
import 'package:todoapp/models/Tasks.dart';
class TasksList extends StatefulWidget {
#override
_TasksListState createState() => _TasksListState();
}
class _TasksListState extends State<TasksList> {
List<Task> task = [
Task(
taskTitle: "Buy Bread",
),
Task(
taskTitle: "Buy Cream",
),
Task(
taskTitle: "Buy Beer",
)
];
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: task.length,
itemBuilder: (context,index){
return TaskTile(isChecked: task[index].isDone,
taskText: task[index].taskTitle,
callBackCheckBox: (bool checkBoxState){
setState(() {
task[index].toogleDone();
});
});
});
}
}
How do I access this list in stateless widget in another dart file? I am new to flutter. And please explain the concept behind it.
Aman Chaudhary to access task list it's done by passing all needed values in Stateless widget contructor in another dart file.
Here you can see that your ListView.builder is returning the stateless widget with all required parameters:
return ListView.builder(
itemCount: task.length,
itemBuilder: (context,index){
return TaskTile(isChecked: task[index].isDone,
taskText: task[index].taskTitle,
callBackCheckBox: (bool checkBoxState){
setState(() {
task[index].toogleDone();
});
});
});
This means whenever the callBackCheckBox is handled the stateful widget is refreshed and the ListView.builder updates his children with all task list updates.
My flutter ListView doesn't update when my setState runs in my State class.
Yes, My main class is a stateful widget, incase anyone was wondering
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() => new _MyAppState();
}
My setState function
setState(() {
if (price == "null") {
items.add("Item ${counter + 1}: error");
print("null");
} else {
items.add("Item ${counter + 1}: $price");
print("$price");
totalPrice += price;
}
counter++;
});
});
Before I placed my ListView within a Container -> Column -> Expanded it was working fine. But after I added it, it stopped updating when my setState ran
body: new Container(
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return Dismissible(
key: Key(item),
onDismissed: (direction) {
setState(() {
items.removeAt(index);
counter--;
});
Scaffold.of(context).showSnackBar(
SnackBar(content: Text("$item deleted")));
},
background: Container(color: Colors.red),
child: ListTile(title: Text('$item')),
);
},
),
),
Could someone who is more knowledgeable in flutter educate me on what's going on here. I don't think adding the ListView into a container should make that much of a difference to how it works right?
Doing computations in setstate somehow caused this problem. Instead I did the computations in build since and I used setstate to only add to the list. It’ll then trigger the build and the rest of the things happen there. This solved my problem