I am fetching a data from api and display it in list using ListView builder, it's working properly on emulator but when I run app on my android phone it is displaying blank screen, here it looks.
below Leave History label, I am displaying data from api, but it looks like this now.
It looks like this on emulator
Here is the code:
Fetching api data and inserting it into list
List<leaveHistory> historyList = [];
var loader = 0;
Future<List<leaveHistory>> _getRecord() async {
Dio dio = new Dio();
var data = {
'username': getname,
'token': getaccesstoken,
};
return dio
.post(localhostUrlLeaveHistoryON, data: json.encode(data))
.then((onResponse) async {
Map<String, dynamic> map = onResponse.data;
List<dynamic> data = map["data"];
for (var historyData in data) {
leaveHistory history = leaveHistory(
historyData["Date"],
historyData["description"],
historyData["type"],
historyData['fromdate'],
historyData["todate"],
historyData["noofleavedays"],
historyData["leave"]);
historyList.add(history);
loader = 0;
}
if (historyList.length == 0) {
loader = 1;
}
return historyList;
}).catchError((onerror) {
loader = 1;
print(onerror.toString());
});
}
display the data using ListView.builder
Widget build(BuildContext context) {
return Scaffold(
appBar: new MyAppBar(
title: Text("Leaves Tracker"),
onpressed: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => Profile()));
}),
drawer: NavigationDrawerWidget(),
body: Stack(overflow: Overflow.visible, children: <Widget>[
Container(
padding: EdgeInsets.fromLTRB(20, 10, 0, 0),
child: Text(
"Leave History",
style: TextStyle(fontSize: 30, fontFamily: 'RaleWay'),
),
),
Container(
padding: EdgeInsets.fromLTRB(10, 30, 0, 0),
child: FutureBuilder(
future: _getRecord(),
builder: (BuildContext context,
AsyncSnapshot<List<leaveHistory>> snapshot) {
if (snapshot.hasData) {
if (historyList.length != 0) {
return Container(
padding: EdgeInsets.fromLTRB(0, 30, 0, 0),
child: Expanded(
child: ListView.builder(
itemCount: snapshot.data.length,
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
Color c = Colors.red;
//changing the avatar color
if (i == 0)
i++;
else if (i == 1) {
c = Colors.blue;
i++;
} else if (i == 2) {
c = Color(int.parse(annualboxcolor));
i = 0;
}
Color c1 = Colors.green;
if (snapshot.data[index].Status == "Approved") {
c1 = Colors.green;
} else if (snapshot.data[index].Status ==
"Not Approved") {
c1 = Colors.red;
} else if (snapshot.data[index].Status ==
"Pending") {
c1 = Color(int.parse(pendingrequestcolor));
}
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
ListTile(
leading: CircleAvatar(
radius: 25,
backgroundColor: c,
child: Container(
padding: EdgeInsets.fromLTRB(
0, 10, 0, 0),
child:
Column(children: <Widget>[
Text(
snapshot
.data[index].noofdays
.toString(),
style: TextStyle(
fontSize: 15.0,
fontWeight:
FontWeight.bold,
color: Colors.white),
),
Text(
int.parse(snapshot
.data[index]
.noofdays) >
1
? "Days"
: "Day",
style: TextStyle(
fontSize: 10.0,
color: Colors.white),
)
]),
)),
title:
Text(snapshot.data[index].type),
isThreeLine: true,
subtitle: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
snapshot.data[index].fromdate +
" To " +
snapshot.data[index].todate,
),
Text(snapshot.data[index].Status,
style: TextStyle(
color: c1,
fontWeight:
FontWeight.bold)),
Text(
snapshot
.data[index].description,
)
],
)),
Divider(
color: Colors.grey,
height: 10,
),
],
));
},
)));
}
}
if (loader == 1) {
print("run");
return Nodatafound();
} else {
return Center(
child: CircularProgressIndicator(
color: Colors.blue[500],
));
}
}),
),
]));
}
}
history class
class leaveHistory {
final String date;
final String fromdate;
final String todate;
final String description;
final String type;
final String Status;
final String noofdays;
leaveHistory(this.date, this.description, this.type, this.fromdate,
this.todate, this.noofdays, this.Status);
}
When i redirect to this screen from drawer it first display this error
DioError [DioErrorType.response]: Http status error [300]
on this catchError this line print(onerror.toString());
Update:
When i connect my phone with my laptop with datacable, it's working fine, but when i install apk problem occurs.
Please help where i am doing wrong.
If #Huthaifa was not the answer you're looking, have you tried to update the value of: localhostUrlLeaveHistoryON?
If you're using something like: http://localhost
Please try to change this to your computer's ip address.
Your error is caused by incorrect use of parent data widget. And that is from using expanded inside container. Remove your Expanded from here:
return Container(
padding: EdgeInsets.fromLTRB(0, 30, 0, 0),
child: Expanded(
child: ListView.builder(
You will also notice that the error in console is gone. Always read the errors and understand them. The error will 100% say, incorrect use of parent widget. Just remove the expanded.
Related
I have several modalBottomSheet in my app to show, most of them have simple widget tree except only one that has 10 DropDownBottom widgets in it
Each one of them load about 200 items, each item is a widget consist of two main widgets a text and an image
when I Press
onPressed: () {
showModalBottomSheet(context: context, builder: picksWidget, isScrollControlled: true);
}
It take about 3 seconds to open load the modalBottomSheet and it just appear into the emulator without the sliding up animation, other modalBottomSheets in the app load perfectly fine, here is an example of the code I use.
import 'package:flutter/material.dart';
import 'package:myapp/data/picked.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:myapp/data/picks.dart';
import 'package:myapp/data/Icons.dart';
Widget buildPickerBottomSheet(BuildContext context) {
return Wrap(children: <Widget>[
PickerList(),
]);
}
class PickerList extends StatelessWidget {
const PickerList({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
padding: EdgeInsets.all(20),
child: Row(
children: <Widget>[
AutoSizeText(
'Pick ',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
textAlign: TextAlign.right,
maxLines: 1,
),
],
),
),
SizedBox(
height: 5,
),
PickerRow(
typeOne: 'vr',
typeTwo: 'er',
type: 'r',
),
PickerRow(
typeOne: 'vq',
typeTwo: 'eq',
type: 'q',
),
PickerRow(
typeOne: 'vw',
typeTwo: 'ew',
type: 'w',
),
PickerRow(
typeOne: 'vz',
typeTwo: 'ez',
type: 'z',
),
PickerRow(
typeOne: 'vy',
typeTwo: 'ey',
type: 'y',
),
SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
child: Text('Add'),
onPressed: () async {
print('added');
},
),
RaisedButton(
child: Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
)
],
),
SizedBox(
height: 50,
)
],
);
}
}
class PickerRow extends StatelessWidget {
final String typeOne;
final String typeTwo;
final String type;
PickerRow({#required this.typeOne, #required this.typeTwo, #required this.type});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 20, right: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
DropDownMenu(
pickType: typeOne,
),
Container(
width: 2,
height: 30,
color: Colors.blue,
),
Container(
height: 30,
width: 30,
child: Image(
image: AssetImage(AppIcons.types[type]),
),
),
Container(
width: 2,
height: 30,
color: Colors.red,
),
DropDownMenu(
pickType: typeTwo,
),
],
),
);
}
}
class DropDownMenu extends StatefulWidget {
//which position will this pick for
final String pickType;
DropDownMenu({#required this.pickType});
#override
_DropDownMenuState createState() => _DropDownMenuState();
}
class _DropDownMenuState extends State<DropDownMenu> {
//get a list of the picks to display in the drop down
static List<DropdownMenuItem> getDropDownItems() {
List<DropdownMenuItem<String>> dropDownItems = [];
for (int i = 0; i < Picks.picksNames.length; i++) {
String pick = Picks.picksNames[i];
var newItem = DropdownMenuItem(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
CircleAvatar(
backgroundImage: AssetImage(AppIcons.picks[pick]),
radius: 15,
),
SizedBox(
width: 4,
),
AutoSizeText(
pick,
maxLines: 1,
),
],
),
value: pick,
);
dropDownItems.add(newItem);
}
return dropDownItems;
}
var items = getDropDownItems();
#override
Widget build(BuildContext context) {
String chosenItem = Picked.selection[widget.pickType];
return DropdownButton<String>(
value: chosenItem,
items: items,
onChanged: (value) {
setState(() {
chosenItem = value;
});
Picked.selection[widget.pickType] = value;
},
);
}
}
I am new to development in general so I appreciate if there is any resources on how to measure and improve performance on flutter apps.
Thanks.
It seems like your bottomModalSheet is too heavy. It loads only after the children have completed building.
Resize the images that you use inside the bottomModalSheet.
Usually the debug app is very slow compared to the release app.
Generate the apk of the app: flutter build apk --split-per-abi.
Install the generated apk build/app/outputs/apk/release/app-armeabi-v7a-release.apk and see if the problem still persists.
You should avoid rendering 2000 widgets at once.
First of all look at ListView widget and it's builder(). Try to refactor your code and put all your items inside this ListView. It will increase your performance a lot due to the reason that ListView.builder() allows you not to store all your widgets in memory all the time.
https://api.flutter.dev/flutter/widgets/ListView/ListView.builder.html
I have been experimenting with Flutter trying to recreate an app that I wrote for Android 5 years ago. But I can't seem to get the layout to work very well.
I have tried a number of different widgets, but each time I try something, it seems like I am taking 2 steps forward and 3 steps back.
The app is going to be vertical position only.
Simple banner image at the top.
A trivia question.
Radio button group with 4 answers.
Then 2 buttons - 'Check Answer' and 'Skip Question'
Bottom bar
I am trying to make sure that the radio buttons and the buttons don't bounce around when the question changes.
This is what I have
This is what I would like
And this is the code that I have been fighting with.
Any help or tips would be greatly appreciated.
class _MyHomePageState extends State<MyHomePage> {
final TriviaBloc _triviaBloc = TriviaBloc();
TriviaQuestion _initTriviaQuestion = new TriviaQuestion('', '', '', '', '', 1, 1);
String _selectedAnswer = "";
TextStyle _answerStyle = new TextStyle(textBaseline: TextBaseline.alphabetic, fontStyle: FontStyle.normal, fontFamily: 'QarmicsansAbridged', fontSize: 16);
#override
void dispose() {
_triviaBloc.dispose();
super.dispose();
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.orangeAccent,
body: SafeArea(
minimum: const EdgeInsets.all(16.0),
child:
Column(children: [
ImageBanner("assets/header_2.jpg"),
Expanded(
child:
StreamBuilder<TriviaQuestion>(
initialData: _triviaBloc.getInitialTriviaQuestion(),
//_initTriviaQuestion,
stream: _triviaBloc.triviaQuestionStream,
//initialData: _triviaBloc.getInitialTriviaQuestion(),
//builder: (BuildContext context, AsyncSnapshot<TriviaQuestion> snapshot)
builder: (BuildContext context, snapshot) {
//_triviaBloc.getTriviaQuestion(snapshot, context);
if (snapshot.data != null) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(height: 15),
Padding(
padding: EdgeInsets.all(2.0),
child: TriviaAutoSizeText(context, snapshot.data.Question),
),
RadioButtonGroup(
labels: <String>[
snapshot.data.IncorrectAnswer1,
snapshot.data.IncorrectAnswer2,
snapshot.data.CorrectAnswer,
snapshot.data.IncorrectAnswer3
],
labelStyle: _answerStyle,
onSelected: (String selected) => _selectedAnswer = selected),
//),
SizedBox(height: 10),
new InkWell(
onTap: () => {_triviaBloc.checkAnswer(QuestionAnswered(_selectedAnswer))},
child: new Container(
width: MediaQuery.of(context).size.width * 0.5,
height: 50.0,
decoration: new BoxDecoration(
color: _butttonInteriorColour,
//Colors.blueAccent,
border:
new Border.all(color: Colors.white, width: 1.0),
borderRadius: new BorderRadius.circular(20.0),
),
child: new Center(
child: new Text(
'Check Answer',
style: new TextStyle(
fontSize: 18.0, color: Colors.white),
),
),
),
),
SizedBox(height: 10),
new InkWell(
onTap: () =>
{_triviaBloc.getTriviaQuestion(snapshot, context)},
child: new Container(
width: MediaQuery.of(context).size.width * 0.5,
height: 50.0,
decoration: new BoxDecoration(
color: _butttonInteriorColour,
//Colors.blueAccent,
border:
new Border.all(color: Colors.white, width: 1.0),
borderRadius: new BorderRadius.circular(20.0),
),
child: new Center(
child: new Text(
'Next Question',
style: new TextStyle(
fontSize: 18.0, color: Colors.white),
),
),
),
),
],
);
} else {
return Center(child: CircularProgressIndicator());
}
;
}),
)]),
),
bottomNavigationBar: buildBottomNav(context),
);
}
}
So you want the 4 button group and the 2 buttons at the bottom without changing position ?
Maybe a Spacer will help with that
if (snapshot.data != null) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max, //replace this to fill everything
children: <Widget>[
SizedBox(height: 15),
Padding(
padding: EdgeInsets.all(2.0),
child: TriviaAutoSizeText(context, snapshot.data.Question),
),
Spacer(), //add the spacer here
RadioButtonGroup(
labels: <String>[
My problem is that : The listview does not appear at the first launch of app but after a hot reload it appears.
It's the first time I meet this bug.
That's my code :
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.black,
body: new ListView.builder(
itemCount: toutesLesCartes.length,
itemBuilder: (context, count) {
Carte carte = toutesLesCartes[count];
String nom = carte.nomCarte;
String image = carte.imageCarte;
return new Dismissible(
key: new Key(nom),
direction: DismissDirection.endToStart,
child: new Container(
margin: EdgeInsets.all(5.0),
child: new Card(
color: Colors.transparent,
elevation: 10.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Image.network(image, width: 150.0, height: 150.0,),
new CustomText(nom, factor: 2.0,),
],
),
),
),
background: new Container(
padding: EdgeInsets.only(right: 20.0),
color: Colors.red,
child: new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new CustomText("Supprimer"),
new Icon(Icons.delete, color: Colors.white,)
],
),
),
onDismissed: (direction) {
setState(() {
toutesLesCartes.removeAt(count);
nombreCarteChoisiValeur--;
});
},
);
}
),
);
}
I have an idea but it's strange : before, I using the Visibility widget so it is possible that it comes from a possible "cache" of the application?
All that will look at this post and help me, thank you very much!
Have a nice day !
Likely because toutesLesCartes.length is 0.
you can check this using the debugger or display something when the length is 0 eg
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.black,
body: new ListView.builder(
itemCount: toutesLesCartes.length, //* place breakpoint here
itemBuilder: (context, count) {
Carte carte = toutesLesCartes[count];
String nom = carte.nomCarte;
String image = carte.imageCarte;
if(toutesLesCartes == null || toutesLesCartes.length == 0){
return CircularProgressIndicator(); //you should see loading animation if list is empty
}
return new Dismissible(
key: new Key(nom),
direction: DismissDirection.endToStart,
child: new Container(
margin: EdgeInsets.all(5.0),
child: new Card(
color: Colors.transparent,
elevation: 10.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Image.network(image, width: 150.0, height: 150.0,),
new CustomText(nom, factor: 2.0,),
],
),
),
),
background: new Container(
padding: EdgeInsets.only(right: 20.0),
color: Colors.red,
child: new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new CustomText("Supprimer"),
new Icon(Icons.delete, color: Colors.white,)
],
),
),
onDismissed: (direction) {
setState(() {
toutesLesCartes.removeAt(count);
nombreCarteChoisiValeur--;
});
},
);
}
),
);
}
The solution would be to use a FutureBuilder
Word of warning: The application will run the build method multiple times, it's good practice to place anything you don't want to be repeatedly called (like database calls) outside of the method.
I'm trying to implement a credit card reader (using some API). For the user to take a picture of the card, I'm presenting the camera preview widget full screen. I would like to mask out an area so the user centers the card there.
He're an illustration of what I have in mind
I was wondering how this masking could be accomplished in flutter? Currently I'm just drawing a BoxDecoration, but that's missing the semi transparent, greyed out area.
Container(
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.width * 0.8 / 1.55,
decoration: new BoxDecoration(
border: Border.all(
color: Colors.white,
width: 2.0,
style: BorderStyle.solid),
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(4.0))),
)
You can try something like this for the time being but it is a very inefficient solution and I am sure there must be a better solution that I too would like to know.
#override
Widget build(BuildContext context) {
if (!controller.value.isInitialized) {
return Container();
}
return Container(
height: MediaQuery.of(context).size.height,
child: Stack(
children: <Widget>[
CustomPaint(
foregroundPainter: P(),
child: CameraPreview(controller),
),
ClipPath(
clipper: Clip(),
child: CameraPreview(controller)),
],
),
);
}
class P extends CustomPainter{
#override
void paint(Canvas canvas, Size size) {
canvas.drawColor(Colors.grey.withOpacity(0.8), BlendMode.dstOut);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
return true;
}
}
class Clip extends CustomClipper<Path>{
#override
getClip(Size size) {
print(size);
Path path = Path()
..addRRect(RRect.fromRectAndRadius(Rect.fromLTWH(10, size.height/2-120, size.width-20, 240), Radius.circular(26)));
return path;
}
#override
bool shouldReclip(oldClipper) {
// TODO: implement shouldReclip
return true;
}
}
I now just went with a custom BoxPainter and first draw the background, then the card area and finally blend the layers together to 'cut out' the center part.
import 'package:flutter/widgets.dart';
class CardDecoration extends Decoration {
#override
BoxPainter createBoxPainter([VoidCallback onChanged]) {
return _CardPainter();
}
}
class _CardPainter extends BoxPainter {
#override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
final clearPaint = new Paint()
..color = Colors.black
..style = PaintingStyle.fill
..blendMode = BlendMode.dstOut;
final bgPaint = new Paint()
..color = Color.fromARGB(150, 0, 0, 0)
..style = PaintingStyle.fill;
final borderPaint = new Paint()
..color = Colors.white.withAlpha(120)
..style = PaintingStyle.stroke
..strokeWidth = 3.0;
final rect = offset & configuration.size;
final cardWidth = 0.8*rect.width;
final cardHeight = cardWidth/1.55;
canvas.saveLayer(Rect.fromLTRB(0, 0, rect.width, rect.height), clearPaint);
canvas.drawPaint(bgPaint);
canvas.saveLayer(Rect.fromLTRB(0, 0, rect.width, rect.height), clearPaint);
canvas.drawRRect(RRect.fromLTRBR(0.1*rect.width, (rect.height-cardHeight)/2, 0.9*rect.width, (rect.height+cardHeight)/2, Radius.circular(8)), bgPaint);
canvas.restore();
canvas.restore();
canvas.drawRRect(RRect.fromLTRBR(0.1*rect.width, (rect.height-cardHeight)/2, 0.9*rect.width, (rect.height+cardHeight)/2, Radius.circular(8)), borderPaint);
}
}
Probably, you can try with columns, rows, and containers :).
Widget getMaskCard(BuildContext context) {
Color _background = Colors.grey.withOpacity(0.2);
return Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Container(
height: MediaQuery.of(context).size.height,
width: 1,
color: _background,
),
),
Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width * 0.8,
child: Column(
children: <Widget>[
Expanded(
child: Container(
color: _background,
),
),
Container(
height: 180,
width: MediaQuery.of(context).size.width * 0.8,
color: Colors.transparent,
),
Expanded(
child: Container(
color: _background,
),
),
],
),
),
Expanded(
child: Container(
height: MediaQuery.of(context).size.height,
width: 1,
color: _background,
),
),
],
)
],
);
}
Example
I have a search TextFied and a Tabbar allowing to search by a number or a text.
When the TabBar is Fired/Tapped the keyboard type should be updated.
The listener allowing to detect the type is correctly fired
The build method is fired and the Textfield is rebuild with the keyboardType
But the keyboard type is not updated
_handleTabSelection() {
if (_tabController.indexIsChanging) {
switch (_tabController.index) {
case kNameIndex:
_searchBy = RunnersSubscriptionsSearchBy.name;
_keyboardType = TextInputType.text;
break;
case kTibibIndex:
_searchBy = RunnersSubscriptionsSearchBy.tibib;
_keyboardType = TextInputType.number;
//_focus.unfocus();
break;
}
setState(() {
;
});
}
}
And the Build searchBar with TextField
_buildSearchBar() {
return Container(
color: Theme.of(context).secondaryHeaderColor,
child: new Padding(
padding: const EdgeInsets.all(5),
child: new Card(
child: new ListTile(
contentPadding: EdgeInsets.only(left: 8, right: 0),
leading: new Icon(Icons.search),
title: new TextField(
keyboardType: _keyboardType,
focusNode: _focus,
controller: _searchTextFieldEditingController,
decoration: new InputDecoration(
hintText: _searchHintTextFromSearchType(_searchBy),
border: InputBorder.none),
),
trailing: new IconButton(
icon: new Icon(Icons.cancel),
onPressed: () {
_searchTextFieldEditingController.clear();
//onSearchTextChanged('');
},
),
),
),
),
);
}
You can use this:
FocusScope.of(context).requestFocus(_blankNode);
Future.delayed(Duration(seconds: 0)).then((v){
FocusScope.of(context).requestFocus(_nameNode);
});
Maybe help you.