Hey guys I need help with removing this button and load data from json file without need to click on that button
Here's code
List _items = [];
// Fetch content from the json file
#override
Widget build(BuildContext context) {
Future readJson() async {
final String response =
await rootBundle.loadString('assets/aaaa.json');
final data = await json.decode(response);
setState(() {
_items = data['first'];
});
}
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(25),
child: Column(
children: [
ElevatedButton(
child: const Text('Load Data'),
onPressed: readJson,
),
// Display the data loaded from sample.json
_items.isNotEmpty
? Expanded(
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
return Card(
margin: const EdgeInsets.all(10),
child: ListTile(
leading: Text(_items[index]["aaaaa"]),
title: Text(_items[index]["aaaaa"]),
subtitle: Text(_items[index]["aaaaaa"]),
),
);
},
),
)
: Container()
],
),
),
);
}
You should check out Future Builder. There are some good examples on that page of how to use the widget, including how to show different widgets depending on if the data was loaded, is in the process of loading, or there was an error. readJson would be the future in your case.
Call initState() before build function
#overrride
initState() {
readJson();
super.initState();
}
Calling the readJson() function just before returning Scaffold will do what you want.
Widget build(BuildContext context) {
//load the json content
readJson();
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(25),
child: Column(
children: [
// Display the data loaded from sample.json
_items.isNotEmpty
? Expanded(
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
return Card(
margin: const EdgeInsets.all(10),
child: ListTile(
leading: Text(_items[index]["aaaaa"]),
title: Text(_items[index]["aaaaa"]),
subtitle: Text(_items[index]["aaaaaa"]),
),
);
},
),
)
: Container()
],
),
),
);}
Related
When I transition to a screen where I get a list of information via an API, it initially gives an error:
_CastError (Null check operator used on a null value)
and only after loading the information, the screen is displayed correctly.
I am declaring the variables like this:
#observable
ObservableFuture<Model?>? myKeys;
#action
getKeys() {
myKeys = repository.getKeys().asObservable();
}
How can I enter the page only after loading the information?
In button action I tried this but to no avail!
await Future.wait([controller.controller.getKeys()]);
Modular.to.pushNamed('/home');
This is the page where the error occurs momentarily, but a short time later, that is, when the api call occurs, the data appears on the screen.
class MyKeyPage extends StatefulWidget {
const MyKeyPage({Key? key}) : super(key: key);
#override
State<MyKeyPage> createState() => _MyKeyPageState();
}
class _MyKeyPageState
extends ModularState<MyKeyPage, KeyController> {
KeyController controller = Modular.get<KeyController>();
Widget countKeys() {
return FutureBuilder(
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
final count =
controller.myKeys?.value?.data!.length.toString();
if (snapshot.connectionState == ConnectionState.none &&
!snapshot.hasData) {
return Text('..');
}
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index) {
return Text(count.toString() + '/5');
});
},
future: controller.getCountKeys(),
);
}
#override
Widget build(BuildContext context) {
Size _size = MediaQuery.of(context).size;
return controller.getCountKeys() != "0"
? TesteScaffold(
removeHorizontalPadding: true,
onBackPressed: () => Modular.to.navigate('/exit'),
leadingIcon: ConstantsIcons.trn_arrow_left,
title: '',
child: Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.only(left: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Keys',
style: kHeaderH3Bold.copyWith(
color: kBluePrimaryTrinus,
),
),
countKeys(),
],
),
),
),
body: Observer(builder: (_) {
return Padding(
padding: const EdgeInsets.only(bottom: 81),
child: Container(
child: ListView.builder(
padding: EdgeInsets.only(
left: 12.0,
top: 2.0,
right: 12.0,
),
itemCount:
controller.myKeys?.value?.data!.length,
itemBuilder: (context, index) {
var typeKey = controller
.myKeys?.value?.data?[index].type
.toString();
var id =
controller.myKeys?.value?.data?[index].id;
final value = controller
.myKeys?.value?.data?[index].value
.toString();
return GestureDetector(
onTap: () {
.
.
},
child: CardMeyKeys(
typeKey: typeKey,
value: value!.length > 25
? value.substring(0, 25) + '...'
: value,
myKeys: pixController
.minhasChaves?.value?.data?[index].type
.toString(),
),
);
},
),
),
);
}),
bottomSheet: ....
)
: TesteScaffold(
removeHorizontalPadding: true,
onBackPressed: () => Modular.to.navigate('/exit'),
leadingIcon: ConstantsIcons.trn_arrow_left,
title: '',
child: Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.only(left: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'...',
style: kHeaderH3Bold.copyWith(
color: kBluePrimaryTrinus,
),
),
],
),
),
),
body: Padding(
padding: const EdgeInsets.only(bottom: 81),
child: Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(
'assets/images/Box.png',
fit: BoxFit.cover,
width: 82.75,
height: 80.91,
),
SizedBox(
height: 10,
),
],
),
), //Center
),
),
bottomSheet: ...
);
}
List<ReactionDisposer> disposers = [];
#override
void initState() {
super.initState();
controller.getKeys();
}
#override
void dispose() {
disposers.forEach((toDispose) => toDispose());
super.dispose();
}
}
Initially the error occurs in this block
value: value!.length > 25
? value.substring(0, 25) + '...'
: value,
_CastError (Null check operator used on a null value)
I appreciate if anyone can help me handle ObservableFuture correctly!
You need to call the "future" adding
Future.wait
(the return type of getKeys) keys=await Future.wait([
controller.getKeys();
]);
The problem is your getKeys function isn't returning anything, so there's nothing for your code to await. You need to return a future in order to await it.
Future<Model?> getKeys() {
myKeys = repository.getKeys().asObservable();
return myKeys!; // Presumably this isn't null anymore by this point.
}
...
await controller.controller.getKeys();
Modular.to.pushNamed('/home');
I'm pretty new at Flutter and trying to make a simple app, where I fetch data with an API and trying to show the results.
This function is responsible to get the data (this function works fine, I get the data):
Connection connection = Connection();
String textValue = '';
Future<void> createlist() async {
List<MoviesByTitle> movieTitle = [];
String response = await connection.getMovieByTitle();
var data = jsonDecode(response);
var results = data['results'];
for (int i = 0; i < results.length; i++) {
movieTitle.add(
MoviesByTitle(
movieId: results[i]['id'],
title: results[i]['original_title'],
shortDescription: results[i]['overview'],
year: results[i]['release_date'],
),
);
}
}
And here comes the screen itself:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Movies app"),
),
body: Column(
children: [
TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Movie title',
),
onSubmitted: (value) {
textValue = value;
},
onChanged: (value) {
textValue = value;
},
),
TextButton(
onPressed: () {
createlist();
},
child: Text("Press"),
),
Expanded(
child: ListView.builder(
itemCount: 30,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
Text("Movie title"),
Text("Short decscription"),
],
),
),
);
}),
),
],
),
bottomNavigationBar: BottomMenu(),
);
}
What I want is: if the TextButton is pressed to show the data of the movies in separate cards. Somehow I can not find a way to create cards dynamically based on the data from the API (maybe I will wrap the ListView builder with a Visibility widget).
Is there any way to change the number of the card and their content dynamically?
You have added the items tothe list movieTitle.. you can use that as a reference to build the ui.. You can try
ListView.builder(
itemCount: movieTitle.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
Text("${movieTitle[index].title}"),
Text("${movieTitle[index].shortDescription}>"),
],
),
),
);
}),
Also you may have to move the movieTitle variable outside the fetch api method so it can be accessed from the ui part too.
I am creating the List of Cards according to the number of toDoId.
toDoController.toDo() is like
toDo = [q1, r4, g4, d4].obs;
And, this is my ListView.builder()
Obx(() {
List _todo = toDoController.toDo();
return ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: _todo.length,
itemBuilder: (BuildContext context, int i) {
var _loading = true;
var _title = 'loading';
getTodoInfo() async {
_title = await toDoController
.getTodoInfo(
_todo[i]
);
_loading = false;
print(_title); // 'Clean!' <--- returns correct title
}
getTodoInfo();
return Container(
height: 150,
width: 150,
child: _loading
? Text(
_title,
)
: Text(
_title,
),
);
},
);
})
I am trying to make each Container calls the http requests to get the title from my database. Get the title and then update to the Text() widget below. However, it doesn't get updated after the value has been returned from the server.
I could make them wait for the request to get the title by using FutureBuilder. I tried with FutureBuilder too. However, FutureBuilder was not also reactive to the variable changes. So, I am trying to do this here. I kinda get the problem. After, the widget is returned, it is not changeable? Is there any way that I can do it with GetX?
Here's an example of using GetX with a Listview.builder.
This example uses a GetBuilder rather than Obx, as I'm not sure using a stream adds anything of benefit. If for some reason observables/streams are needed, numbers can be updated to be an .obs and the update() calls should be removed and GetBuilder replaced by GetX or Obx. If someone asks, I'll add that as an alternate example.
The GetBuilder wraps the ListView.builder and only the ListView will be rebuilt, not the entire widget tree / page.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class ListDataX extends GetxController {
List<int> numbers = List<int>.from([0,1,2,3]);
void httpCall() async {
await Future.delayed(Duration(seconds: 1),
() => numbers.add(numbers.last + 1)
);
update();
}
void reset() {
numbers = numbers.sublist(0, 3);
update();
}
}
class GetXListviewPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
ListDataX dx = Get.put(ListDataX());
print('Page ** rebuilt');
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(
flex: 8,
child: GetBuilder<ListDataX>(
builder: (_dx) => ListView.builder(
itemCount: _dx.numbers.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('Number: ${_dx.numbers[index]}'),
);
}),
),
),
Expanded(
flex: 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RaisedButton(
child: Text('Http Request'),
onPressed: dx.httpCall,
),
RaisedButton(
child: Text('Reset'),
onPressed: dx.reset,
)
],
)
)
],
),
),
);
}
}
Obx / Streams version
Here's the above solution using Rx streams & Obx widget.
class ListDataX2 extends GetxController {
RxList<int> numbers = List<int>.from([0,1,2,3]).obs;
void httpCall() async {
await Future.delayed(Duration(seconds: 1),
() => numbers.add(numbers.last + 1)
);
//update();
}
void reset() {
numbers = numbers.sublist(0, 3);
//update();
}
}
class GetXListviewPage2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
ListDataX2 dx = Get.put(ListDataX2());
print('Page ** rebuilt');
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(
flex: 8,
child: Obx(
() => ListView.builder(
itemCount: dx.numbers.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('Number: ${dx.numbers[index]}'),
);
}),
),
),
Expanded(
flex: 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RaisedButton(
child: Text('Http Request'),
onPressed: dx.httpCall,
),
RaisedButton(
child: Text('Reset'),
onPressed: dx.reset,
)
],
)
)
],
),
),
);
}
}
I've not tested it due to the fact that I don't have a complete sample but I think this is what you are looking for:
FutureBuilder<String>(
future: toDoController.getTodoInfo(_todo[i]),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
return Container(
height: 150,
width: 150,
child: Text(snapshot.data),
);
} else if (snapshot.hasError) {
return Text('Error');
} else {
return CircularProgressIndicator();
}
},
),
This is the code you need to return for every item of list builder.
I am using Future builder in app and its working fine but when data load and when i am showing it in Text widget its showing this error
Class '_InternalLinkedHashMap<String, dynamic>' has no instance getter 'approved_value'.
Receiver: _LinkedHashMap len:31
Tried calling: approved_value
My code
class _ClaimsScreenState extends State<ClaimsScreen> {
#override
initState() {
super.initState();
doSomeAsyncStuff();
}
Future<List> doSomeAsyncStuff() async {
final storage = new FlutterSecureStorage();
String value = await storage.read(key: 'health_card');
print(value);
String url2 =
'api.com';
final response2 = await http.get(url2);
var Data = json.decode(response2.body);
print(Data);
var DisplayData = Data["records"];
return DisplayData;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('IGI GENERAL INSURANCE'),
),
body: FutureBuilder<List>(
future: doSomeAsyncStuff(),
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
if (snapshot.hasData) {
print('ss');
print(snapshot.data);
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Row(
children: <Widget>[
Text('Approved Value:'),
Text(snapshot.data[index].approved_value)
],
),
Row(
children: <Widget>[
Text('Patient Name:'),
Text(snapshot.data[index].patient_name)
],
)
],
),
Row(
children: <Widget>[
Row(
children: <Widget>[
Text('Claimed Value:'),
Text(snapshot.data[index].claimed_value)
],
),
Row(
children: <Widget>[
Text('status:'),
Text(snapshot.data[index].patient_name)
],
)
],
)
],
),
);
});
} else if (snapshot.hasError) {
return Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Error: ${snapshot.error}'),
);
} else {
return Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Awaiting result...'),
);
}
}),
);
}
}
I am using listview builder in future builder because the values are in array and need to show all in text widget
I am not sure why its showing this error if remove the value its working fine just when i show the values in Text widget then its showing error.
snapshot.data[index] returns a Map. The Map class does not have an approved_value getter. You likely intended to do snapshot.data[index]['approved_value'] instead.
How to control listviewbuilder from outside the listview in flutter?
In a textfield I can use a controller like so: controller: Textcontroller. Can I do something similar in listviewbuilder to clear all the objects in it?
So to be exact. My code looks something like this
Expanded(
child: new ListView.builder(
itemCount: List.length,
itemBuilder: (context,index){
return new Card(
//all stuff with data
),
),
);
},
....
How would I do so that when called from another function it removes all the items in the listview?
You need a Store class that holds this list along with list manipulation methods, then you can use provider for example to access that class and render that list.
You can define a variable in the state of your widget:
var _clear = false;
When this variable is true, the list will be cleared and when it's false, the list will be displayed. You can use setState to toggle this variable. Setting the itemCount of the ListView.builder to 0 clears the list.
Full code:
var _clear = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(30.0),
child: FlatButton(
child: Text('clear'),
color: Colors.pinkAccent,
onPressed: () {
setState(() {
_clear = true;
});
},
),
),
FlatButton(
child: Text('add'),
color: Colors.greenAccent,
onPressed: () {
setState(() {
_clear = false;
});
},
),
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount: _clear ? 0 : 100,
itemBuilder: (context, index) {
return Container(
height: 300,
child: Card(
child: Center(
child: Text('$index'),
),
),
);
},
),
),
],
),
);
}