I have fetched the details of the planet i.e. (NOM) No. of moons, Gravity and Density from an API.
And i have stored these details in an array PlanetInfo. And i want to display it using text widget like Text("${PlanetInfo[1]}", style: TextStyle(color: Colors.white)). but it is giving me an erro: RangeError (index): Invalid value: Valid value range is empty: 1
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:xperience/Models/planets.dart';
class PlanetDescNoMGD extends StatelessWidget {
final Planet planeteee;
List PlanetInfo = [];
getPlanetData() async {
var url =
"https://api.le-systeme-solaire.net/rest/bodies/${planeteee.planetApi}";
final uri = Uri.parse(url);
final response = await http.get(uri);
final body = response.body;
final jsondata = jsonDecode(body);
PlanetInfo.add(jsondata["moons"].length);
PlanetInfo.add(jsondata["gravity"]);
PlanetInfo.add(jsondata["density"]);
}
PlanetDescNoMGD({Key? key, required this.planeteee}) : super(key: key);
#override
void initState() {
this.getPlanetData();
}
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const Text(
"No. of moons",
style:
TextStyle(color: Colors.white, fontWeight: FontWeight.w700),
),
const SizedBox(
height: 12,
),
Text("${PlanetInfo[1]}", style: TextStyle(color: Colors.white))
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const Text(
"Gravity",
style:
TextStyle(color: Colors.white, fontWeight: FontWeight.w600),
),
const SizedBox(
height: 12,
),
Text("${PlanetInfo[1]}" + " m/s²",
style: TextStyle(color: Colors.white))
//Text(${num} + " m/s²", style: TextStyle(color: Colors.white))
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const Text(
"Density",
style:
TextStyle(color: Colors.white, fontWeight: FontWeight.w600),
),
const SizedBox(
height: 12,
),
Text("${PlanetInfo[2]}" + " g/cm3",
style: TextStyle(color: Colors.white))
],
),
],
);
}
}
I need a solution.
You do not know how to work with async functions. Please learn it.
You are making async request to API and directly trying to use respond from API. It is not possible. What is happening is your List PlanetInfo is empty when build is called. so you are getting error.
Stateless widgets does not have initState! I see you are trying to override initState that does not exist.
You need stateful widget to use setState.
Here is, with minimal change working version of your code. You need to use setState after data is loaded and change isLoaded = true.
Here is solution:
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:xperience/Models/planets.dart';
class PlanetDescNoMGD extends StatefulWidget {
PlanetDescNoMGD({Key? key, required this.planeteee}) : super(key: key);
final Planet planeteee;
#override
State<PlanetDescNoMGD> createState() => _PlanetDescNoMGDState();
}
class _PlanetDescNoMGDState extends State<PlanetDescNoMGD> {
List PlanetInfo = [];
bool isLoaded = false;
getPlanetData() async {
var url =
"https://api.le-systeme-solaire.net/rest/bodies/${widget.planeteee.planetApi}";
final uri = Uri.parse(url);
final response = await http.get(uri);
final body = response.body;
final jsondata = jsonDecode(body);
PlanetInfo.add(jsondata["moons"].length);
PlanetInfo.add(jsondata["gravity"]);
PlanetInfo.add(jsondata["density"]);
setState(() {
isLoaded = true;
});
}
#override
void initState() {
super.initState();
getPlanetData();
}
#override
Widget build(BuildContext context) {
return !isLoaded
? const Center(child: CircularProgressIndicator())
: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const Text(
"No. of moons",
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.w700),
),
const SizedBox(
height: 12,
),
Text("${PlanetInfo[1]}",
style: TextStyle(color: Colors.white))
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const Text(
"Gravity",
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.w600),
),
const SizedBox(
height: 12,
),
Text("${PlanetInfo[1]}" + " m/s²",
style: TextStyle(color: Colors.white))
//Text(${num} + " m/s²", style: TextStyle(color: Colors.white))
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const Text(
"Density",
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.w600),
),
const SizedBox(
height: 12,
),
Text("${PlanetInfo[2]}" + " g/cm3",
style: TextStyle(color: Colors.white))
],
),
],
);
}
}
You have got 2 things to fix there.
Class.
Do not use PlanetInfo as a list of ints.
Make full use of defining a class: encapsulating data and improving readability.
Define the class as follows
PlanetInfo{
int noOfMoons;
double gravity;
double density;
PlanetInfo({
required this.noOfMoons,
required this.gravity,
required this.density,
});
}
Declare a member in state.
List PlanetInfo = [];//remove this
late final PlanetInfo myPlanet;// use this
Now create an object in your fetch call and assign it to state member.
final jsondata = jsonDecode(body);
PlanetInfo tempPlanet=PlanetInfo(
noOfMoons: jsondata["moons"].length,
gravity: jsondata["gravity"],
density: jsondata["density"],
);
myPlanet=x; // use setState() if required
and display in widgets using attributes.
//rest of code
Text("${planetInfo.noOfMoons}",
//rest of code
Text("${planetInfo.gravity}}" + " m/s²",'
//rest of code
Text("${planetInfo.density}" + " g/cm3",
//rest of code
Displaying API data.
You can either use FutureBuilder to wait on building UI until api call is done, or do as #hiloliddin suggested (using isLoaded member and building ui based on its value).
In your current implementation you were getting the error because UI was built before api call finished and the list PlanetInfo was empty.
Related
I am currently building a botcoin ticker app that returns a list of currencies to a dropDown menu .
I am getting the following errors:-
error: The body might complete normally, causing 'null' to be returned, but the return type is a potentially non-nullable type. (body_might_complete_normally at [bitcoin_ticker] lib\price_screen.dart:10)
error: The argument type 'List<DropdownMenuItem>' can't be assigned to the parameter type 'List<DropdownMenuItem>?'. (argument_type_not_assignable at [bitcoin_ticker] lib\price_screen.dart:60)
Here is my code
import 'package:flutter/material.dart';
import 'coin_data.dart';
int i = 0;
class PriceScreen extends StatefulWidget {
#override
_PriceScreenState createState() => _PriceScreenState();
}
List<DropdownMenuItem> getDropDownItems() {
List<DropdownMenuItem<String>> dropdownItem = [];
for (String currency in currenciesList) {
DropdownMenuItem(
child: Text(currency), //
value: currency,
);
}
}
class _PriceScreenState extends State<PriceScreen> {
late String selectedCurrency = 'USD';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('🤑 Coin Ticker'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(18.0, 18.0, 18.0, 0),
child: Card(
color: Colors.lightBlueAccent,
elevation: 5.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: Padding(
padding: EdgeInsets.symmetric(vertical: 15.0, horizontal: 28.0),
child: Text(
'1 BTC = ? USD',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
),
),
),
),
),
Container(
height: 150.0,
alignment: Alignment.center,
padding: EdgeInsets.only(bottom: 30.0),
color: Colors.lightBlue,
child: DropdownButton<String>(
value:selectedCurrency,//Default value
items: getDropDownItems(),
onChanged:(value){//it is like an on Pressed Button
setState(() {
selectedCurrency = value!;
print(selectedCurrency);
});
},
),
),
],
),
);
}
}
The warning shows because the getDropDownItems doesn't returning anything from the function. Also you are not updating the list in the for loop. you can refer the following code
List<DropdownMenuItem> getDropDownItems() {
List<DropdownMenuItem> dropdownItem = currenciesList
.map((currency) => DropdownMenuItem(
child: Text(currency), //
value: currency,
))
.toList();
return dropdownItem;
}
I try to collect data in firestore from my Flutter app. With the following code: my question is how to display a error message when the user didn't choose an item on DropdownMenuItem?
body: Form(
key: _formKeyValue,
child: ListView(
padding: EdgeInsets.symmetric(horizontal: 12.0),
children: <Widget>[
SizedBox(height: 20.0),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
DropdownButton(
items: _specialite
.map((value) => DropdownMenuItem(
child: Text(
value,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.black),
),
value: value,
))
.toList(),
onChanged: (selectedAccountType) {
print('$selectedAccountType');
setState(() {
medicalType = selectedAccountType;
});
},
value: medicalType,
isExpanded: false,
hint: Text(
'choisissez la spécialité',
style: TextStyle(color: Colors.black),
),
)
],
),
....
....
i used this answer https://stackoverflow.com/a/59746301/15400156 but nothing displayed on screen.
You can make the first menu item called "Select xxx", which is also the default item (index 0), then you can check the index when the user hit "Submit" button.
You can achieve this in different ways.Remove value property from DropDownButton and initialize medicalType with String medicalType;. Then on submit button press, check if medicalType is null or not. Below is the full code.
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
List<String> _specialite = ["abc", "def", 'ghi'];
String medicalType;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Form(
// key: _formKeyValue,
child: ListView(
padding: EdgeInsets.symmetric(horizontal: 12.0),
children: <Widget>[
SizedBox(height: 20.0),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
DropdownButton(
items: _specialite
.map((value) => DropdownMenuItem(
child: Text(
value,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
value: value,
))
.toList(),
onChanged: (String selectedAccountType) {
print('$selectedAccountType');
setState(() {
medicalType = selectedAccountType;
});
},
// value: medicalType,
isExpanded: false,
hint: Text(
'choisissez la spécialité',
style: TextStyle(color: Colors.black),
),
)
],
),
ElevatedButton(
onPressed: () {
if (medicalType == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("please choose medical type")));
}
},
child: Text("submit"))
]),
));
}
}
I'm using Flutter to build a Chat Application.
In my message bubbles, I display the message text, the date, and an icon to show if the message was read or not. The text of the message is poorly displayed. It goes to the next line after just one or two words instead of filling the full width of the bubble.
Container(
padding: EdgeInsets.symmetric(
horizontal: 15.0, vertical: 10.0),
width: MediaQuery.of(context).size.width * 0.65,
margin: EdgeInsets.only(top: 8.0, bottom: 8.0, left: 80.0, right: 10),
decoration: BoxDecoration(
color: primaryColor.withOpacity(.1),
borderRadius: BorderRadius.circular(20)),
child: Column(
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Container(
child: Text(
documentSnapshot.data['text'],
style: TextStyle(
color: Colors.black87,
fontSize: 16.0,
fontWeight: FontWeight.w600,
),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
documentSnapshot.data["time"] != null
? DateFormat.MMMd().add_jm()
.format(documentSnapshot.data["time"].toDate())
.toString()
: "",
style: TextStyle(
color: secondryColor,
fontSize: 13.0,
fontWeight: FontWeight.w600,
),
),
SizedBox(width: 5),
documentSnapshot.data['isRead'] == false
? Icon(
Icons.done,
color: secondryColor,
size: 15,
)
: Icon(
Icons.done_all,
color: primaryColor,
size: 15,
)
],
),
Short answer
Currently, you are using a Row Widget to display the message text as the first child and then the date and read icon as the second child:
Instead, you should use a Column Widget.
Full solution
After going back and forth with Julien
1. Domain Layer
class ChatEntry {
final String text;
final DateTime date;
final bool read;
final bool sent;
ChatEntry({
this.text,
this.date,
this.read,
this.sent,
});
}
2. Chat Bubble
class Bubble extends StatelessWidget {
final ChatEntry entry;
const Bubble({Key key, this.entry}) : super(key: key);
#override
Widget build(BuildContext context) {
return Align(
alignment: entry.sent ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
padding: kBubblePadding,
decoration: BoxDecoration(
color: (entry.sent ? kSentColor : kReceivedColor)
.withOpacity(entry.read ? kReadOpacity : 1),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(kBorderRadius),
topRight: Radius.circular(kBorderRadius),
bottomRight: Radius.circular(entry.sent ? 0.0 : kBorderRadius),
bottomLeft: Radius.circular(entry.sent ? kBorderRadius : 0.0),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
entry.sent ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
Text(entry.text, style: kBubbleTextStyle),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
DateFormat('MMMd – kk:mm').format(entry.date),
style: TextStyle(fontSize: kBubbleMetaFontSize),
),
if (entry.read) ...[
const SizedBox(width: 5),
Icon(Icons.done, size: kBubbleMetaFontSize)
]
],
),
],
),
),
);
}
}
3. Chat Conversation
class Conversation extends StatelessWidget {
final List<ChatEntry> entries;
const Conversation({Key key, this.entries}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: entries
.map(
(entry) => Padding(
padding: const EdgeInsets.all(8.0),
child: Bubble(entry: entry),
),
)
.toList(),
);
}
}
4. Application
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Chat Demo',
home: Scaffold(
body: SingleChildScrollView(
child: Conversation(entries: getChatEntries()),
),
),
),
);
}
Full Source Code for easy copy-paste
Together with random data generation.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:faker/faker.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Chat Demo',
home: Scaffold(
body: SingleChildScrollView(
child: Conversation(entries: getChatEntries()),
),
),
),
);
}
class Conversation extends StatelessWidget {
final List<ChatEntry> entries;
const Conversation({Key key, this.entries}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: entries
.map(
(entry) => Padding(
padding: const EdgeInsets.all(8.0),
child: Bubble(entry: entry),
),
)
.toList(),
);
}
}
class Bubble extends StatelessWidget {
final ChatEntry entry;
const Bubble({Key key, this.entry}) : super(key: key);
#override
Widget build(BuildContext context) {
return Align(
alignment: entry.sent ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
padding: kBubblePadding,
decoration: BoxDecoration(
color: (entry.sent ? kSentColor : kReceivedColor)
.withOpacity(entry.read ? kReadOpacity : 1),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(kBorderRadius),
topRight: Radius.circular(kBorderRadius),
bottomRight: Radius.circular(entry.sent ? 0.0 : kBorderRadius),
bottomLeft: Radius.circular(entry.sent ? kBorderRadius : 0.0),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
entry.sent ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
Text(entry.text, style: kBubbleTextStyle),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
DateFormat('MMMd – kk:mm').format(entry.date),
style: TextStyle(fontSize: kBubbleMetaFontSize),
),
if (entry.read) ...[
const SizedBox(width: 5),
Icon(Icons.done, size: kBubbleMetaFontSize)
]
],
),
],
),
),
);
}
}
// DOMAIN
class ChatEntry {
final String text;
final DateTime date;
final bool read;
final bool sent;
ChatEntry({
this.text,
this.date,
this.read,
this.sent,
});
}
// CONFIG
const kSentColor = Color(0xff03bd85);
const kReceivedColor = Color(0xff0251d6);
const kReadOpacity = .3;
const kBorderRadius = 15.0;
const kBubblePadding = const EdgeInsets.symmetric(
horizontal: 15.0,
vertical: 10.0,
);
const kBubbleTextStyle = const TextStyle(
color: Colors.black87,
fontSize: 16.0,
fontWeight: FontWeight.w600,
);
const kBubbleMetaFontSize = 11.0;
// RANDOM DATA
final Random random = Random.secure();
final faker = new Faker();
List<ChatEntry> getChatEntries() {
final nbMessages = random.nextInt(17) + 3;
final lastRead = random.nextInt(nbMessages);
DateTime date = DateTime.now();
return List.generate(
nbMessages,
(index) {
date = date.subtract(Duration(minutes: random.nextInt(30)));
return ChatEntry(
text: faker.lorem
.words(2 + random.nextInt(random.nextBool() ? 3 : 15))
.join(' '),
date: date,
read: index >= lastRead,
sent: random.nextBool(),
);
},
).reversed.toList();
}
Just 3 weeks into flutter-dart programming so I'm still a rookie.
I'm implementing a user Settings screen where the user's information is displayed.
The user can modify their information such as their first/last name and address.
I want the user's current name to be displayed as an initial value and as soon as the user modifies the field, I want to keep the change visible inside the TextField until they press the 'Update' button (see animation below). However, whenever the user changes, for example their first name, the initial value is shown again and their changes are lost (see animation below).
My TextField code for first name (last name and address are implemented similarly):
TextField(
onChanged: (text) => {},
textAlign: TextAlign.center,
controller: _firstNameController..text = userRep.firstName,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp('[a-zA-Z]'))
],
onSubmitted: (text) {
setState(() {
_firstNameController.text = text;
});
},
style: GoogleFonts.lato(
fontSize: 16.0
)
)
and the controller is defined at the beginning of the class:
final TextEditingController _firstNameController = TextEditingController();
currently under testing so I use a user mocking with defaulted values.
here is the app's current behavior:
any ideas, please?
edit: after #AndreaCostanzo1 's answer, I'm adding more info and code portion about my work:
The TextField in question is inside the build method of
class _UserSettingsScreenState extends State<UserSettingsScreen>:
class _UserSettingsScreenState extends State<UserSettingsScreen> {
final GlobalKey<ScaffoldState> _scaffoldKeyUserScreenSet = new GlobalKey<ScaffoldState>();
final TextEditingController _firstNameController = TextEditingController();
final TextEditingController _lastNameController = TextEditingController();
final TextEditingController _addressController = TextEditingController();
final TextEditingController _creditCardController = TextEditingController();
#override
Widget build(BuildContext context) {
return Material(
color: Colors.lightGreen,
child: Consumer<UserRepository>(
builder:(context, userRep, _) {
return Scaffold(
resizeToAvoidBottomInset: false,
resizeToAvoidBottomPadding: false,
backgroundColor: Colors.lightGreen[600],
key: _scaffoldKeyUserScreenSet,
appBar: AppBar(
backgroundColor: Colors.lightGreen[900],
leading: IconButton(
icon: Icon(Icons.menu),
onPressed: null //TODO: implement navigation drawer
),
title: Text("Settings"),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(height: 20,),
CircularProfileAvatar(
userRep.avatarURL ??
'https://www.flaticon.com/svg/static/icons/svg/848/848043.svg',
borderColor: Colors.red,
radius: MediaQuery.of(context).size.height * 0.1,
initialsText: Text(
"Press to change",
textAlign: TextAlign.center,
style: GoogleFonts.lato()
),
onTap: () {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext context) {
return Container(
height: 117,
child: Column(
textDirection: TextDirection.ltr,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
ListTile(
tileColor: Colors.white,
leading: Icon(
Icons.photo_camera,
color: Colors.red,
),
title: Text("Take a new photo",
style: GoogleFonts.lato(),
),
onTap: () async {
PickedFile photo = await ImagePicker()
.getImage(source: ImageSource.camera);
if (null == photo) {
Scaffold.of(context).showSnackBar(
SnackBar(content:
Text("No image selected",
style: GoogleFonts.notoSans(fontSize: 18.0),
),
behavior: SnackBarBehavior.floating,
)
);
} else {
setState(() {
userRep.avatarURL = photo.path;
});
}
},
),
ListTile(
tileColor: Colors.white,
leading: Icon(
Icons.photo_size_select_actual_rounded,
color: Colors.red,
),
title: Text("Select from gallery",
style: GoogleFonts.lato(),
),
onTap: () async {
PickedFile photo = await ImagePicker()
.getImage(source: ImageSource.gallery);
if (null == photo) {
Scaffold.of(context).showSnackBar(
SnackBar(content:
Text("No image selected",
style: GoogleFonts.notoSans(fontSize: 18.0),
),
behavior: SnackBarBehavior.floating,
)
);
} else {
setState(() {
userRep.avatarURL = photo.path;
});
}
},
),
],
),
);
}
); //showModalBottomSheet
},
),
SizedBox(height: 30,),
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: Container(
width: MediaQuery.of(context).size.width * 0.25,
height: MediaQuery.of(context).size.height * 0.1,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: SizedBox(
height: 200.0,
width: 100,
child: Text('First name',
style: GoogleFonts.montserrat(
fontSize: 16.0
),
textAlign: TextAlign.center,
),
),
),
Expanded(
flex: 3,
child: SizedBox(
height: 200.0,
width: MediaQuery.of(context).size.width * 0.5 - 10,
child: TextField(
onChanged: (text) => {},
textAlign: TextAlign.center,
controller: _firstNameController..text = userRep.firstName,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp('[a-zA-Z]'))
],
onSubmitted: (text) {
setState(() {
_firstNameController.text = text;
});
},
style: GoogleFonts.lato(
fontSize: 16.0
)
),
),
),
],
),
),
),
and the UserRepository mock looks like this:
thank you everybody in advance!
After you call setState the widget tree is rebuilt. Since you gave us just a smaller fragment of code, I can't tell for sure if this is the portion of code that generates this issue.
controller: _firstNameController..text = userRep.firstName,
However, from the video I can tell you that, after calling submit (when the widget tree is rebuilt) you are setting back the value to its original state.
If you want to give an initial state to the textfield, do this:
initState(){
super.initState();
_firstNameController=TextEditingController();
_firstNameController.text = userRep.firstName,
}
And in the text field just use
controller: _firstNameController,
Also, remember to dismiss the controller when the widget is disposed:
dispose(){
_firstNameController.dispose();
}
I've been following this youtube video to help me return data from an online JSON file into a list view. I've altered the code slightly, including a change in URL to the JSON file and due to this, the code now requests different data.
Something tells me it's because the JSON type I want to use isn't compatible with the code I've used, but I don't know why and might be wrong. I use the original 'StarWarsData', 'StarWarsState' that the author of the provided video used just to minimise the differences in my code.
Thanks, Jake
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
void main() {
runApp(MaterialApp(
home: StarWarsData(),
));
}
class StarWarsData extends StatefulWidget {
#override
StarWarsState createState() => StarWarsState();
}
class StarWarsState extends State<StarWarsData> {
final String url = "https://api.coinmarketcap.com/v2/listings/";
List data;
Future<String> getSWData() async {
var res = await http
.get(Uri.encodeFull(url), headers: {"Accept": "application/json"});
setState(() {
var resBody = json.decode(res.body);
data = resBody["data"];
});
return "Success!";
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Star Wars Starships"),
backgroundColor: Colors.deepPurpleAccent,
),
body: ListView.builder(
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, int index) {
return new Container(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Card(
child: Container(
padding: EdgeInsets.all(15.0),
child: Row(
children: <Widget>[
Text("Id: "),
Text(data[index]["id"],
style: TextStyle(
fontSize: 18.0, color: Colors.black87)),
],
)),
),
Card(
child: Container(
padding: EdgeInsets.all(15.0),
child: Row(
children: <Widget>[
Text("Name: "),
Text(data[index]["name"],
style: TextStyle(
fontSize: 18.0, color: Colors.red)),
],
)),
),
Card(
child: Container(
padding: EdgeInsets.all(15.0),
child: Row(
children: <Widget>[
Text("Symbol: "),
Text(data[index]["symbol"],
style: TextStyle(
fontSize: 18.0, color: Colors.black87)),
],
)),
),
],
),
),
);
},
),
);
}
#override
void initState() {
super.initState();
this.getSWData();
}
}
EDIT
The question has now been fixed, but in case anyone was interested, here is the error I was previously encountering:
I/flutter (31850): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY
╞═══════════════════════════════════════════════════════════I/flutter
(31850): type 'int' is not a subtype of type 'String' where I/flutter
(31850): int is from dart:core I/flutter
(31850): String is from dart:core
Issue lies here,
Text(data[index]["id"],
where "id" field is an integer and you are using it directly in place of a String.
Change it to,
Text('${data[index]["id"]}',