How to parse json from the api in flutter? - json

I want to parse JSON from the link in Flutter. Here is my api structure.
Here is the model.dart class which is generated using jsontodart.com.
Here is the services.dart class:
import 'dart:convert';
import 'package:get_api/model.dart';
import 'package:http/http.dart' as http;
class Services {
//
static const String url = 'http://...............';
static Future<List<User>> getUsers() async {
try {
final response = await http.get(url);
if (response.statusCode == 200) {
final body = jsonDecode(response.body);
//print(body);
final Iterable json = body;
return json.map((user) => User.fromJson(user)).toList();
} else {
return List<User>();
}
} catch (e) {
return List<User>();
}
}
}
Here is main.dart
import 'package:flutter/material.dart';
import 'package:get_api/model.dart';
import 'package:get_api/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: JsonParseDemo(),
);
}
}
class JsonParseDemo extends StatefulWidget {
//
JsonParseDemo() : super();
#override
_JsonParseDemoState createState() => _JsonParseDemoState();
}
class _JsonParseDemoState extends State<JsonParseDemo> {
//
List<User> _users;
bool _loading;
#override
void initState() {
super.initState();
_loading = true;
Services.getUsers().then((users) {
setState(() {
_users = users;
_loading = false;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_loading ? 'Loading...' : 'Users'),
),
body: Container(
color: Colors.white,
child: ListView.builder(
itemCount: _users == null ? 0 : _users.length,
itemBuilder: (context, index) {
User user = _users[index];
return ListTile(
title: Text(user.clientInfos[index].cLINTNAME),
subtitle: Text(user.clientInfos[index].cLINTEMAIL),
);
},
),
),
);
}
}
But the problem is that it only shows one list. How can I show all the list? I don't understand why it is not working.

I've modified some files and created a new file, here my code:
model.dart
class User {
...
String show() {
if (success) {
return '''
"clientInfos" has ${clientInfos.length} items
"projectInfos" has ${projectInfos.length} items
"ticketPriorityInfos" has ${ticketPriorityInfos.length} items
"ticketTypeInfos" has ${ticketTypeInfos.length} items
"ticketModeInfos" has ${ticketModeInfos.length} items
"ticketStatusInfos" has ${ticketStatusInfos.length} items
"ticketTitleInfos" has ${ticketTitleInfos.length} items
''';
} else {
return "Nothing to show, what a pity!!!";
}
}
}
net_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:read_json2/models/model.dart';
class NetService {
static Future fetchJsonData(String url) {
return
http.get(url)
.then((response) => response?.statusCode == 200 ? jsonDecode(response.body) : null)
.catchError((err) => print(err));
}
static Future<User> fetchTickesInfo() {
return fetchJsonData('http://203.130.133.166/ATI-ERP2/ticket-lookup')
.then((response) => (response != null) ? User.fromJson(response[0]) : null)
.catchError((err) => print('OMFG!!! an error: $err'));
}
}
home_page.dart
EDIT: forgot list items, but it is solved
import 'package:flutter/material.dart';
import 'package:read_json2/models/model.dart';
import 'package:read_json2/services/net_service.dart';
class HomePage extends StatelessWidget {
/* ---------------------------------------------------------------------------- */
const HomePage({Key key}) : super(key: key);
/* ---------------------------------------------------------------------------- */
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hi!'),
centerTitle: true,
),
body: FutureBuilder<User>(
future: NetService.fetchTickesInfo(),
builder: (context, snapshot) => snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.clientInfos.length,
itemBuilder: (context, index) => Card(
child: Column(
children: [
Text(snapshot.data.clientInfos[index]?.cLINTNAME ?? '--'),
Text(snapshot.data.clientInfos[index]?.cLINTEMAIL ?? '--'),
],
),
),
)
: snapshot.hasError
? Text('Something was wrong!: ${snapshot.error}')
: Text('Loading...'),
),
);
}
}

Your problem is how you compute the index for the list. In your JSON you have an array with length of 1, that is your user, now this user has 57 clientInfos that you're using. I assume you want to display those, so you should have something like this:
#override
Widget build(BuildContext context) {
User user;
if(_users != null && _users.length > 0) {
user = _users[index];
}
return Scaffold(
appBar: AppBar(
title: Text(_loading ? 'Loading...' : 'Users'),
),
body: Container(
color: Colors.white,
child: ListView.builder(
itemCount: user?.clientInfos?.length ?? 0,
itemBuilder: (context, index) {
return ListTile(
title: Text(user.clientInfos[index].cLINTNAME),
subtitle: Text(user.clientInfos[index].cLINTEMAIL),
);
},
),
),
);
}

Related

unable to parse json data from local database in flutter 'an error has occurred !'

Future<List<Institute>> fetchInstitute(http.Client client) async {
final response = await client
.get(Uri.parse('http://192.168.1.5/tkydatabase/fun/anem.php'));
// Use the compute function to run parseInstitute in a separate isolate.
return compute(parseInstitute, response.body);
}
List<Institute> parseInstitute(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Institute>((json) => Institute.fromJson(json)).toList();
}
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
const appTitle = 'Isolate Demo';
return const MaterialApp(
title: appTitle,
home: MyHomePage(title: appTitle),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: FutureBuilder<List<Institute>>(
future: fetchInstitute(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('An error has occurred!'),
);
} else if (snapshot.hasData) {
return InstitutesList(institute: snapshot.data!);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
}
class InstitutesList extends StatelessWidget {
const InstitutesList({super.key, required this.institute});
final List<Institute> institute;
#override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: institute.length,
itemBuilder: (context, index) {
return Text(institute[index].instituteName);
},
);
}
}
enter image description here
The data from JSON, so it must have a key and value
is this code help you?
var json =
'{"body":[{"id":1,"instituteName":"value","instituteWilaya":"value"},{"id":2,"instituteName":"value","instituteWilaya":"value"}]}';
var map = jsonDecode(json) as Map<String, dynamic>;
var entityList = map['body'] as List;
var result = List.generate(
entityList.length, (index) => Institute.fromJson(entityList[index]));
print(result);

Why do I get 'data2' has not been initiliazed error here?

It makes no sense at this code. I am trying to grab some strings from the ALREADY INITIALIZED from the futurebuilder data, but can't do so. It gives me the data2 has not been initialized error. Here is data and data2, they are almost similiar, data works fine but data2 doesn't. Why is that?
late List<Album> data;
late List<String> data2;
Here is my code:
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
const List<String> list = <String>['One', 'Two', 'Three', 'Four'];
Future<List<Album>> fetchAlbum() async {
final response = await http.get(
Uri.parse('https://my-json-server.typicode.com/fluttirci/testJson/db'));
print(response);
Map<String, dynamic> userMap = jsonDecode(response.body);
if (response.statusCode == 200) {
return (userMap['employees'] as List)
.map((e) => Album.fromJson(e))
.toList();
} else {
throw Exception('Failed to load album');
}
}
class Album {
final int userId;
final int id;
final String title;
Album(this.userId, this.id, this.title);
Album.fromJson(Map<String, dynamic> json)
: userId = json['userId'],
id = json['id'],
title = json['title'];
Map<String, dynamic> toJson() => {
'userId': userId,
'id': id,
'title': title,
};
}
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late Future<Album> futureAlbum;
late Future<List<Album>> user;
late List<Album> data;
late List<String> data2;
#override
void initState() {
super.initState();
user = fetchAlbum();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Fetch Data Example'),
),
body: Column(children: <Widget>[
Expanded(
child: const DropdownButtonExample(),
),
Expanded(
child: FutureBuilder<List<Album>>(
future: user,
builder: (context, snapshot) {
if (snapshot.hasData) {
data = snapshot.data ?? [];
print('${data[4].title}');
return ListView.builder(
itemBuilder: (context, index) {
data2[index] = data[index].title;
return Column(children: [
Text(data[index].title ?? ""),
]);
},
itemCount: data.length,
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return Center(
child: const CircularProgressIndicator(),
);
},
),
)
]),
),
);
}
}
class DropdownButtonApp extends StatelessWidget {
const DropdownButtonApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('DropdownButton Sample')),
body: const Center(
child: DropdownButtonExample(),
),
),
);
}
}
class DropdownButtonExample extends StatefulWidget {
const DropdownButtonExample({super.key});
#override
State<DropdownButtonExample> createState() => _DropdownButtonExampleState();
}
class _DropdownButtonExampleState extends State<DropdownButtonExample> {
String dropdownValue = list.first;
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? value) {
// This is called when the user selects an item.
setState(() {
dropdownValue = value!;
});
},
items: list.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}
For RangeError issue, your data2 is empty so you can search for index and pass your value to it, just add your string to it normally it will put that in right index, so change your listview's builder to this:
return ListView.builder(
itemBuilder: (context, index) {
data2.add(data[index].title);//<--- change this
return Column(children: [
Text(data[index].title ?? ""),
]);
},
itemCount: data.length,
);

Why Does my code return circularprogressindicator always, it is not fetching my data

When i run the application it doesnt fetch the data
import 'package:fakeapi/homepage.dart';
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:fakeapi/album.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.lightGreen,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
builder: ((context, snapshot) {
if (snapshot.hasError) {
throw Exception("ERROR");
} else if (snapshot.hasData) {
ListView.builder(
itemBuilder: ((context, index) {
return Text(snapshot.data!.title);
}),
itemCount: 1,
);
}
return Center(
child: CircularProgressIndicator(),
);
}),
future: getAlbums(),
),
);
}
}
Future<Album> getAlbums() async {
final response = await http
.get(Uri.parse("https://jsonplaceholder.typicode.com/albums/10"));
if (response.statusCode == 200) {
return Album.fromJson(jsonDecode(response.body));
} else {
throw Exception("hey");
}
}
// To parse this JSON data, do
//
// final album = albumFromJson(jsonString);
List<Album> albumFromJson(String str) =>
List<Album>.from(json.decode(str).map((x) => Album.fromJson(x)));
String albumToJson(List<Album> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
This is how i coded my class
class Album {
Album({
required this.userId,
required this.id,
required this.title,
});
int userId;
int id;
String title;
factory Album.fromJson(Map<String, dynamic> json) => Album(
userId: json["userId"],
id: json["id"],
title: json["title"],
);
Map<String, dynamic> toJson() => {
"userId": userId,
"id": id,
"title": title,
};
}
I tried to fetch data and display it
import 'package:fakeapi/homepage.dart';
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:fakeapi/album.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.lightGreen,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
builder: ((context, snapshot) {
if (snapshot.hasError) {
throw Exception("ERROR");
} else if (snapshot.hasData) {
ListView.builder(
itemBuilder: ((context, index) {
return Text(snapshot.data!.title);
}),
itemCount: 1,
);
}
return Center(
child: CircularProgressIndicator(),
);
}),
future: getAlbums(),
),
);
}
}
Future<Album> getAlbums() async {
final response = await http
.get(Uri.parse("https://jsonplaceholder.typicode.com/albums/10"));
if (response.statusCode == 200) {
return Album.fromJson(jsonDecode(response.body));
} else {
throw Exception("hey");
}
}
// To parse this JSON data, do
//
// final album = albumFromJson(jsonString);
List<Album> albumFromJson(String str) =>
List<Album>.from(json.decode(str).map((x) => Album.fromJson(x)));
String albumToJson(List<Album> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Album {
Album({
required this.userId,
required this.id,
required this.title,
});
int userId;
int id;
String title;
factory Album.fromJson(Map<String, dynamic> json) => Album(
userId: json["userId"],
id: json["id"],
title: json["title"],
);
Map<String, dynamic> toJson() => {
"userId": userId,
"id": id,
"title": title,
};
}
change your FutureBuilder to this:
builder: ((context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
default:
if (snapshot.hasError) {
throw Exception("ERROR");
} else {
return ListView.builder(
itemBuilder: ((context, index) {
return Text(snapshot.data!.title);
}),
itemCount: 1,
);
}
}
}),
How do you know it doesn't fetch the data? With your current code you always return the CircularProgressIndicator when there isn't an error.
You have to return the ListView builder. I.e.
return ListView.builder(
itemBuilder: ((context, index) {
return Text(snapshot.data!.title);
}),
itemCount: 1,
);
Your current api return a single model,
.....typicode.com/albums/10"
You wont get a list here. You can return single item from this like changing ListView to Text.
if (snapshot.hasData) {
return Text("${snapshot.data?.title}");
You can follow this widget to receive full list
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late final future = getAlbums();
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<Album>?>(
future: future,
builder: (context, snapshot) {
print(snapshot);
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data?.length,
itemBuilder: ((context, index) {
final model = snapshot.data![index];
return Text("${model.title}");
}),
);
}
return Center(
child: CircularProgressIndicator(),
);
}),
);
}
}
Future<List<Album>?> getAlbums() async {
final response =
await http.get(Uri.parse("https://jsonplaceholder.typicode.com/albums/"));
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as List?;
return data?.map((e) => Album.fromJson(e)).toList();
} else {
throw Exception("hey");
}
}

flutter: bottomNavigationBar cannot switch between html pages using WebView

Goal
I use bottomNavigationBar to switch between a few pages. Two of the pages use WebView to show local html files.
Problem
I found that when I switch between these pages, the pages with pure flutter widgets can load perfectly. But the HTML pages will only show the initially loaded page on switching.
For example, If I have two navigator buttons leading to Page1 and Page2, then at runtime, if I tapped Page1 button first, then tapped Page2 button, then the WebView would still show Page1 instead of Page2. This is wrong.
Code
Here is the HTML page code I use
class LocalLoader {
Future<String> loadLocal(String filename) async {
return await rootBundle.loadString('assets/doc/$filename');
}
}
class HtmlPage extends StatelessWidget {
final String htmlFile;
HtmlPage({
#required this.htmlFile
});
#override
Widget build(BuildContext context) {
return Container(
child: FutureBuilder<String>(
future: LocalLoader().loadLocal(htmlFile),
builder: (context, snapshot) {
if (snapshot.hasData) {
return WebView(
initialUrl: Uri.dataFromString(snapshot.data,
mimeType: 'text/html',
// CAUTION
// - required for non-ascii chars
encoding: Encoding.getByName("UTF-8")
).toString(),
javascriptMode: JavascriptMode.unrestricted,
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
} else {
print('undefined behaviour');
}
return CircularProgressIndicator();
},
),);
}
}
Then with my bottomNavigationBar, I handle the tap event:
class MyFlutterView extends StatefulWidget {
#override
_MyFlutterViewState createState() => _MyFlutterViewState();
}
class _MyFlutterViewState extends State<MyFlutterView> {
final Keys keys = Keys();
int _iSelectedDrawerItem = 3; // self
int _iSelectedNavItem = 0;
static List<Widget> _widgetOptions = <Widget>[
MyFlutterPlaceholder(title: 'Index 0: MyFlutter'),
MyPage(htmlFile: 'page1.html'),
MyPage(htmlFile: 'page2.html'),
];
void _onItemTapped(int index) {
setState(() {
_iSelectedNavItem = index;
});
}
#override
Widget build(BuildContext context) {
final deviceSize = MediaQuery.of(context).size;
final appBar = AppBar(
backgroundColor: WidgetColors.menubar,
title: Text('MyFlutter'),
);
return Scaffold(
appBar: appBar,
endDrawer: NavDrawer(
keys: keys,
iSelectedDrawerItem: _iSelectedDrawerItem,
),
body: Container(
decoration: BoxDecoration(
gradient: WidgetColors.canvas,
),
child: _widgetOptions.elementAt(_iSelectedNavItem),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex : _iSelectedNavItem,
type: BottomNavigationBarType.fixed,
backgroundColor: WidgetColors.menubar,
fixedColor: WidgetColors.myColor,
// selectedItemColor: WidgetColors.myColor,
unselectedItemColor: Colors.white,
selectedIconTheme: IconThemeData(color: WidgetColors.myColor),
// unselectedIconTheme: IconThemeData(color: Colors.white),
items: [
BottomNavigationBarItem(
label: 'MyFlutter',
icon: Icon(Icons.build)
),
BottomNavigationBarItem(
label: 'Page1-HTML',
icon: Icon(Icons.help,),
),
BottomNavigationBarItem(
label: 'Page2-HTML',
icon: Icon(Icons.info_outline_rounded),
),
],
onTap: _onItemTapped),
);
}
}
I've also tried StatefulWidgets but the problem persists.
Workaround
The only workaround I have right now is to derive from the HtmlPage class for every single page I have, like this:
class Page1 extends HtmlPage {
Page1() : super(htmlFile: 'page1.html');
}
class Page2 extends HtmlPage {
Page2() : super(htmlFile: 'page2.html');
}
After this, the HTML pages will switch and load as expected.
Question
How should I fix this? Should I work on loading the HTML file more explicitly? I thought setState would handle the loading for me automatically and this certainly applies to the pure flutter widget page (MyFlutterPlaceholder class in the code above).
Also, I ensured that the url loading was called every time I switch page through the nav bar.
You can copy paste run full code below
I simulate this case with the full code below
Step 1: Use AutomaticKeepAliveClientMixin
class _WebViewKeepAlive extends State<WebViewKeepAlive>
with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
Step 2: Do not direct put function in future attribute, future: LocalLoader().loadLocal(htmlFile), Please use below way
Future<String> _future;
#override
void initState() {
_future = _getUrl(widget.url);
super.initState();
}
return FutureBuilder(
future: _future,
Step 3: I use PageView in this case
working demo
full code
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyPortalPage(title: 'Flutter Demo Home Page'),
);
}
}
class MyPortalPage extends StatefulWidget {
MyPortalPage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyPortalPageState createState() => _MyPortalPageState();
}
class _MyPortalPageState extends State<MyPortalPage> {
int _currentIndex = 0;
PageController _pageController = PageController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SizedBox.expand(
child: PageView(
controller: _pageController,
children: <Widget>[
Page1(),
WebViewKeepAlive(url: "https://flutter.dev/"),
WebViewKeepAlive(url: "https://stackoverflow.com/"),
Center(child: Text("Settings")),
],
onPageChanged: (int index) {
print("onPageChanged");
setState(() {
_currentIndex = index;
});
},
),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
selectedItemColor: Colors.amber[800],
unselectedItemColor: Colors.blue,
onTap: (index) {
print("onItemSelected");
setState(() => _currentIndex = index);
_pageController.jumpToPage(index);
},
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.apps),
label: 'Challenges',
),
BottomNavigationBarItem(
icon: Icon(Icons.people),
label: 'Users',
),
BottomNavigationBarItem(
icon: Icon(Icons.message),
label: 'Messages',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Settings',
),
],
),
);
}
}
class Page1 extends StatefulWidget {
const Page1({Key key}) : super(key: key);
#override
_Page1State createState() => _Page1State();
}
class _Page1State extends State<Page1> with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
super.build(context);
return ListView.builder(itemBuilder: (context, index) {
return ListTile(
title: Text('Lorem Ipsum'),
subtitle: Text('$index'),
);
});
}
#override
bool get wantKeepAlive => true;
}
class WebViewKeepAlive extends StatefulWidget {
final String url;
WebViewKeepAlive({Key key, this.url}) : super(key: key);
#override
_WebViewKeepAlive createState() => _WebViewKeepAlive();
}
class _WebViewKeepAlive extends State<WebViewKeepAlive>
with AutomaticKeepAliveClientMixin {
Future<String> _future;
#override
bool get wantKeepAlive => true;
Future<String> _getUrl(String url) async {
await Future.delayed(Duration(seconds: 1), () {});
return Future.value(url);
}
#override
void initState() {
_future = _getUrl(widget.url);
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
super.build(context);
return FutureBuilder(
future: _future,
builder: (context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
return Text(
'${snapshot.error}',
style: TextStyle(color: Colors.red),
);
} else {
return WebView(
initialUrl: snapshot.data,
javascriptMode: JavascriptMode.unrestricted,
);
}
}
});
}
}

Flutter: Loading json data within second page Flutter Class timing out without error

I've generated a listView from the Pokemon API of a list of Pokemon, I then have onTap events to trigger a new page/class, where I'm passing the name and URL from the API to the new secondPage Class/Screen.
I need to make a second request in this new page because the API Url needs to change to grab specific details but my request seems to be timing out..
Here is my code: If loaded into a new project the first screen should function fine, loading a bunch of Pokemon and their API specific URLs into a listView.
I can successfully pass the name and URL onto the second screen because they do appear in the Appbar.
However when loading the new json data it seems to be timing out without any error.
Does anyone have any advice for a newbie trying to get his footing?
import 'dart:async';
import 'dart:convert';
import 'package:basic_utils/basic_utils.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController editingController = TextEditingController();
String url = 'https://pokeapi.co/api/v2/pokemon/?limit=151';
List data;
Future<String> makeRequest() async {
var response = await http
.get(Uri.encodeFull(url), headers: {"Accept": "application/json"});
setState(() {
var extractData = json.decode(response.body);
data = extractData["results"];
});
}
#override
void initState() {
this.makeRequest();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Pokemon List'),
),
body: Container(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(15.0),
child: new TextField(
onChanged: (value) {
},
decoration: InputDecoration(
labelText: "Search",
hintText: "Search",
contentPadding: const EdgeInsets.all(10.0),
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(6.0))
),
),
),
),
Expanded(
child: new ListView.builder(
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, i) {
return new ListTile(
title: new Text(StringUtils.capitalize(data[i]["name"])),
subtitle: new Text(data[i]["url"]),
// leading: new CircleAvatar(
// backgroundImage:
// new NetworkImage(data[i]["picture"]["thumbnail"]),
// ),
onTap: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) =>
new SecondPage(data[i])
)
);
},
);
}
),
),
],
),
),
);
}
}
// Class for getting Specific Details on SecondPage
class Post {
final String name;
final int weight;
Post({this.name, this.weight});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
name: json['name'],
weight: json['weight'],
);
}
}
// New Request for Specific Details
class SecondPage extends StatelessWidget {
SecondPage(this.data);
final data;
Future<Post> fetchPost() async {
final response =
await http.get('https://pokeapi.co/api/v2/pokemon/' + data["name"]);
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON.
return Post.fromJson(json.decode(response.body));
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
Future<Post> post;
#override
void initState() {
fetchPost();
post = fetchPost();
}
#override
Widget build(BuildContext context) =>
new Scaffold(
appBar: new AppBar(
title: new Text(data["name"] + ' - ' + data["url"])),
body: new Center(
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.all(30.0),
child: FutureBuilder<Post>(
future: post,
builder: (context, snapshot) {
if (snapshot.hasData) {
Text(snapshot.data.name);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
],
),
)
);
}
Man, I rewrite your code as I like
import 'dart:async';
import 'dart:convert';
//import 'package:basic_utils/basic_utils.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController editingController = TextEditingController();
String url = 'https://pokeapi.co/api/v2/pokemon/?limit=151';
List data;
Future<String> makeRequest() async {
var response = await http.get(Uri.encodeFull(url), headers: {"Accept": "application/json"});
setState(() {
var extractData = json.decode(response.body);
data = extractData["results"];
});
}
#override
void initState() {
super.initState();
makeRequest();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Pokemon List'),
),
body: Container(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(15.0),
child: TextField(
onChanged: (value) {},
decoration: InputDecoration(
labelText: "Search",
hintText: "Search",
contentPadding: const EdgeInsets.all(10.0),
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(6.0))),
),
),
),
Expanded(
child: ListView.builder(
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, i) {
return ListTile(
title: Text(data[i]["name"].toString().toUpperCase()),
subtitle: Text(data[i]["url"]),
// leading: CircleAvatar(
// backgroundImage:
// NetworkImage(data[i]["picture"]["thumbnail"]),
// ),
onTap: () {
Navigator.push(
context, MaterialPageRoute(builder: (BuildContext context) => SecondPage(data[i])));
},
);
}),
),
],
),
),
);
}
}
class SecondPage extends StatefulWidget {
Map data;
SecondPage(this.data);
_SecondState createState() => _SecondState();
}
class _SecondState extends State<SecondPage> {
#override
void initState() {
super.initState();
_fetchPost();
}
Map post;
bool isLoad = true;
_fetchPost() async {
setState(() {
isLoad = true;
});
var url = widget.data["url"];
debugPrint(url);
final response = await http.get(url);
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON.
post = json.decode(response.body.toString());
setState(() {
isLoad = false;
});
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.data["name"] + ' - ' + widget.data["url"])),
body: _buildPokemon(context),
);
}
Widget _buildPokemon(BuildContext context) {
if (isLoad) return Center(child: CircularProgressIndicator());
return Container(
padding: EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(post['name']),
Text(post['weight'].toString()),
Text(post['height'].toString()),
Image.network(post['sprites']['front_default'])
],
),
);
}
}