I need your help, below is my simple code trying to get data from API, I'm using flutter and postman for testing I don't know what exactly is wrong but i'm getting the error (The method [] was called on null.) Anyone who can help!!
class FetchTarget extends StatefulWidget {
#override
// _FetchTargetState createState() => _FetchTargetState();
// return fetchTarget();
// };
State<StatefulWidget> createState() {
return _FetchTargetState();
}
}
class _FetchTargetState extends State<FetchTarget> {
fetchTarget() async {
var res = await http.get("http://192.168.1.00:8000/api/taarifa");
if (res.statusCode == 200) {
var obj = json.decode(res.body);
return obj;
}
}
#override
void initState() {
fetchTarget();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder(
future: fetchTarget(),
builder: (context, snapshot) {
if (snapshot != null) {
return ListView.builder(
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data[index]['name']),
subtitle: Text(snapshot.data[index]['date']),
);
},
);
} else {
return CircularProgressIndicator();
}
},
),
));
}
}
in the end i expect it to return lists of taarifa and display in in listview in my mobile
Rember that for a correct implementation of FutureBuilder you must pay attention that the async method must not called more than once.
Moreover I noticed that you don't return anything if there is an error.
That could be the problem:
This is a simple solution:
import 'dart:convert';
import 'package:flutter/material.dart';
class FetchTarget extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _FetchTargetState();
}
}
class _FetchTargetState extends State<FetchTarget> {
Future _fetched;
#override
void initState() {
_fetched = _fetchTarget();
super.initState();
}
Future<dynamic> _fetchTarget() async {
var res = await http.get("http://192.168.1.00:8000/api/taarifa");
if (res.statusCode == 200) {
var obj = json.decode(res.body);
return obj;
}else {
return <String, dynamic>{} ; //an empty map as error, but you must check better if the http get returned an exception
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder(
future: _fetched,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemBuilder: (context, index) {
itemCount : snapshot.data.length,
return ListTile(
title: Text(snapshot.data[index]['name']),
subtitle: Text(snapshot.data[index]['date']),
);
},
);
} else {
return CircularProgressIndicator();
}
},
),
));
}
}
Another better solution is to use Flutter Hooks.
Here after a little example:
class MyWidget extends HookWidget {
#override
Widget build(BuildContext context) {
final future = useMemoized(() {
_fetchTarget();
});
return FutureBuilder<String>(
future: future,
builder: (context, snapshot) {
return ....;
}
);
}
}
Related
Hello fellow Flutter Devs
I am pretty new to Flutter and I am building a bottom navigation bar with three pages and on one page the will be a list with items from an API using the bloc architecture. However I am getting the error in the title located to the ListPage().
ProviderNotFoundException (Error: Could not find the correct Provider above this ListPage Widget
ListPage():
class ListPage extends StatelessWidget {
const ListPage({super.key});
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => ListCubit(
repository: context.read<ListRepository>(),
)..fetchList(),
child: const ItemListView(),
);
}
}
class ItemListView extends StatelessWidget {
const ItemListView({super.key});
#override
Widget build(BuildContext context) {
final state = context.watch<ListCubit>().state;
switch (state.status) {
case ListStatus.failure:
return const Center(child: Text('Oops something went wrong!'));
case ListStatus.success:
return ItemView(items: state.items);
case ListStatus.loading:
return const Center(child: CircularProgressIndicator());
}
}
}
class ItemView extends StatelessWidget {
const ItemView({super.key, required this.items});
final List<Item> items;
#override
Widget build(BuildContext context) {
return items.isEmpty
? const Center(child: Text('no content'))
: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return ItemTile(
item: items[index],
);
},
itemCount: items.length,
);
}
}
class ItemTile extends StatelessWidget {
const ItemTile({
super.key,
required this.item,
});
final Item item;
#override
Widget build(BuildContext context) {
return Material(
child: ListTile(
leading: Text('#${item.id}'),
title: Text(item.position),
),
);
}
}
The ListPage() is in the HomePage():
class HomePage extends StatelessWidget {
const HomePage({super.key});
static Route<void> route() {
return MaterialPageRoute<void>(builder: (_) => const HomePage());
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: BlocBuilder<BottomNavigationBloc, BottomNavigationState>(
builder: (BuildContext context, BottomNavigationState state) {
if (state is PageLoading) {
return Center(child: CircularProgressIndicator());
}
if (state is ListPageLoaded) {
return ListPage();
}
if (state is SettingsPageLoaded) {
return SettingsPage(
biometrics: state.biomentrics,
version: state.version,
env: state.env,
);
}
return Container();
},
),
bottomNavigationBar:
BlocBuilder<BottomNavigationBloc, BottomNavigationState>(
builder: (BuildContext context, BottomNavigationState state) {
return BottomNavigationBar(
...
And last but not least, my app.dart:
class App extends StatelessWidget {
const App({
super.key,
required this.authenticationRepository,
required this.userRepository,
required this.listRepository,
required this.settingsRepository,
});
final AuthenticationRepository authenticationRepository;
final UserRepository userRepository;
final ListRepository listRepository;
final SettingsRepository settingsRepository;
#override
Widget build(BuildContext context) {
return RepositoryProvider.value(
value: authenticationRepository,
child: MultiBlocProvider(
providers: [
BlocProvider(
create: (_) => AuthenticationBloc(
authenticationRepository: authenticationRepository,
userRepository: userRepository,
),
),
BlocProvider(
create: (_) => BottomNavigationBloc(
listPageRepository: listRepository,
settingsPageRepository: settingsRepository))
],
child: const AppView(),
));
}
}
class AppView extends StatefulWidget {
const AppView({super.key});
#override
State<AppView> createState() => _AppViewState();
}
class _AppViewState extends State<AppView> {
final _navigatorKey = GlobalKey<NavigatorState>();
NavigatorState get _navigator => _navigatorKey.currentState!;
#override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: _navigatorKey,
builder: (context, child) {
return BlocListener<AuthenticationBloc, AuthenticationState>(
listener: (context, state) {
switch (state.status) {
case AuthenticationStatus.authenticated:
_navigator.pushAndRemoveUntil<void>(
HomePage.route(),
(route) => false,
);
break;
case AuthenticationStatus.unauthenticated:
_navigator.pushAndRemoveUntil<void>(
LoginPage.route(),
(route) => false,
);
break;
case AuthenticationStatus.unknown:
break;
}
},
child: child,
);
},
onGenerateRoute: (_) => SplashPage.route(),
);
}
}
As the error says it could not find the right provider I probably need to add it somewhere in my project but i don't know where because I added the ListRepository in my App(). Do I need to add in the _AppViewState() as well?
Thanks for your help and I will give additional information in the comments if you need so.
So I have this code and I take an image from Internet with webscrapper, the problem is that when I try to take the image with the basic URl without the http:// behind it don't work and when I add it I don't have any error but I got a black screen on my emulator and I can't see this value of the image on my terminal even if I know the value is not null.
If someone can help I will be very greatful thank you very much !
class ContentScreen extends StatefulWidget {
const ContentScreen({Key? key}) : super(key: key);
#override
_ContentScreenState createState() => _ContentScreenState();
}
class _ContentScreenState extends State<ContentScreen> {
List<Map<String,dynamic>>? contentPages;
bool Data = false;
Future<void> getcontent() async{
final webscraper = WebScraper("https://manhuas.net/");
String TempRoute = "manhua/what-harm-would-my-girl-do-manhua/what-harm-would-my-girl-do-chapter-1/";
if (await webscraper.loadWebPage(TempRoute)){
contentPages = webscraper.getElement("div.page-break.no-gaps > img ", ["data-src"]);
setState(() {
Data = true;
});
print(contentPages);
}
}
#override
void initState() {
// TODO: implement initState
super.initState();
getcontent();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getcontent(),
builder: (context, snapshot) {
return Scaffold(
body: Data? Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: contentPages!.length,
itemBuilder: (context,index ) {
return Image.network(contentPages![index]['attributes']['src'].toString().trim(),
fit: BoxFit.fitWidth,loadingBuilder: (context , child, loadingprogress){
if (loadingprogress != null) return child;
return Center(
child: CircularProgressIndicator(),
);
},);
},
)
)
: Center(
child: CircularProgressIndicator(
color: Constants.mygreen,
)
));
}
);
}
}
And this is a screen of my screen for more details:
Please check the below code it's working perfectly
import 'package:flutter/material.dart';
import 'package:web_scraper/web_scraper.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'OverlayEntry Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: ContentScreen(),
);
}
}
class ContentScreen extends StatefulWidget {
const ContentScreen({Key? key}) : super(key: key);
#override
_ContentScreenState createState() => _ContentScreenState();
}
class _ContentScreenState extends State<ContentScreen> {
List<Map<String, dynamic>>? contentPages;
bool Data = false;
Future<void> getcontent() async {
final webscraper = WebScraper("https://manhuas.net/");
String TempRoute =
"manhua/what-harm-would-my-girl-do-manhua/what-harm-would-my-girl-do-chapter-1/";
if (await webscraper.loadWebPage(TempRoute)) {
contentPages =
webscraper.getElement("div.page-break.no-gaps > img ", ["data-src"]);
setState(() {
Data = true;
});
print(contentPages);
}
}
#override
void initState() {
// TODO: implement initState
super.initState();
getcontent();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getcontent(),
builder: (context, snapshot) {
return Scaffold(
body: Data
? Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: contentPages!.length,
itemBuilder: (context, index) {
return Image.network(
contentPages![index]['attributes']
['data-src']
.toString()
.trim(),
fit: BoxFit.fitWidth,
);
},
))
: Center(
child: CircularProgressIndicator(
color: Colors.green,
)));
});
}
}
I am trying to setup a new sort dropdown button which changes the sort order of the entries that are being fetched from the database.
I can sort the list on page load but I cannot change the sort order using the dropdown options.
The code complies without errors.
Any advice what I need to change to make this code work?
class NewList extends StatefulWidget {
#override
State<StatefulWidget> createState() => new _State();
}
class _State extends State<NewList> {
static dates;
List<Entry> _entryList = [];
List<Entry> _sort = [];
StreamController<int> _postsController;
List<Option> options = [];
Option _selectedOption;
void _select(Option option) {
setState(() {
_selectedOption = option;
});
if (_selectedOption.title =='Oldest first') {
_sort.sort((a, b) {
return b.id.compareTo(a.id);
});
} else if (_selectedOption.title =='Newest first') {
_sort.sort((a, b) {
return a.id.compareTo(b.id);
});
}
}
#override
void initState() {
_postsController = new StreamController();
fetchReport();
super.initState();
}
fetchReport() {
fecthEntries(dates.id.toString(), dates.from, dates.to)
.then((value) => {
_entryList.addAll(value),
_postsController.add(1),
setState(() {})
});
}
#override
Widget build(BuildContext context) {
dates = ModalRoute.of(context).settings.arguments;
options = <Option>[
Option(
title: 'Oldest first',
Option(
title: 'Newest first',
];
_selectedOption = options[0];
return Scaffold(
appBar: AppBar(
title: Text('Title'),
actions: <Widget>[
PopupMenuButton<Option>(
onSelected: _select,
icon: Icon(Icons.sort),
itemBuilder: (BuildContext context) {
return options.map((Option option) {
return PopupMenuItem<Option>(
value: option,
child: Text(option.title),
);
}).toList();
},
),
],
),
body: StreamBuilder<int>(
stream: _postsController.stream,
builder: (BuildContext context, AsyncSnapshot<int> result) {
if (result.hasData) {
return showAll();
}
}),
);
}
Widget showAll() {
//This is where I can sort the list onload and setup the default sort
_entryList.sort((a, b) {
return b.id.compareTo(a.id);
});
return ListView.builder(
itemCount: _entryList.length,
itemBuilder: (context, index) {
final entry = _entryList[index];
return reportRow(entry, context);
},
);
}
}
class Option {
const Option({this.title});
final String title;
}
I would try to wrap the sort logic with a setState() as well. Have you tried?
I am trying to create a search widget in flutter, for list I used the application list using device_apps (https://pub.dev/packages/device_apps). But Im getting an error "type 'Future' is not a subtype of type 'List?'" . To create the search widget I used help from this link.
Here is my code:
import 'dart:async';
import 'package:device_apps/device_apps.dart';
import 'package:flutter/material.dart';
class SearchWidget extends StatelessWidget {
SearchWidget({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: <Widget>[
TextField(onChanged: _filter),
StreamBuilder<List<Application>>(
initialData: lelist(),
stream: _stream,
builder:
(BuildContext context, AsyncSnapshot<List<Application>> snapshot) {
print(snapshot.data);
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Text(snapshot.data[index].appName);
},
);
},
)
],
),
);
}
}
StreamController<List<Application>> _streamController = StreamController<List<Application>>();
Stream<List<Application>> get _stream => _streamController.stream;
_filter(String searchQuery) {
List<Application> _filteredList = lelist()
.where((Application app) => app.appName.toLowerCase().contains(searchQuery.toLowerCase()))
.toList();
_streamController.sink.add(_filteredList);
}
lelist() async {
List<Application> _dataFromQuerySnapShot = await DeviceApps.getInstalledApplications(includeAppIcons: true);
_dataFromQuerySnapShot.sort((a, b) {
return a.appName.toString().toLowerCase().compareTo(b.appName.toString().toLowerCase());
});
return _dataFromQuerySnapShot;
}
I think there it is unnecessary to load and sort the installed apps on every keystroke and therefore put all of this logic into initState. This also makes your _filter function synchronous.
import 'dart:async';
import 'package:device_apps/device_apps.dart';
import 'package:flutter/material.dart';
class SearchWidget extends StatefulWidget {
SearchWidget({Key key}) : super(key: key);
#override
_SearchWidgetState createState() => _SearchWidgetState();
}
class _SearchWidgetState extends State<SearchWidget> {
final _streamController = StreamController<List<Application>>();
List<Application> _dataFromQuerySnapShot;
Stream<List<Application>> get _stream => _streamController.stream;
#override
void initState() {
DeviceApps.getInstalledApplications(includeAppIcons: true)
.then((List<Application> apps) {
_dataFromQuerySnapShot = apps;
_dataFromQuerySnapShot.sort((a, b) {
return a.appName
.toString()
.toLowerCase()
.compareTo(b.appName.toString().toLowerCase());
});
_streamController.sink.add(_dataFromQuerySnapShot);
});
super.initState();
}
#override
void dispose() {
_streamController.close();
super.dispose();
}
_filter(String searchQuery) {
final filteredApplications = _dataFromQuerySnapShot
.where((Application app) =>
app.appName.toLowerCase().contains(searchQuery.toLowerCase()))
.toList();
_streamController.sink.add(filteredApplications);
}
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: <Widget>[
TextField(onChanged: _filter),
StreamBuilder<List<Application>>(
stream: _stream,
builder: (BuildContext context,
AsyncSnapshot<List<Application>> snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
return snapshot.data.isEmpty
? Center(child: Text('Empty'))
: ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Text(snapshot.data[index].appName);
},
);
},
)
],
),
);
}
}
I think you need to add asList after calling your future.
lelist() async {
List<Application> _dataFromQuerySnapShot = await
DeviceApps.getInstalledApplications(includeAppIcons: true);
_dataFromQuerySnapShot.sort((a, b) {
return
a.appName.toString().toLowerCase().compareTo(b.appName.toString().toLowerCase());
}) as List;
return _dataFromQuerySnapShot;
}
I am fetching json from here: https://api.myjson.com/bins/1g3xpe, in my flutter app.
Here is the code (I have removed unrelated part).The issue is, FutureBuilder's builder part is never getting called. This line debugPrint(snapshot.hasData.toString()); is never getting called.
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:nirmithi/objects/project.dart';
import 'dart:convert';
class Projects extends StatefulWidget {
#override
ProjectsState createState() => new ProjectsState();
}
class ProjectsState extends State<Projects> {
#override
Widget build(BuildContext context) {
return Scaffold(
primary: true,
appBar: EmptyAppBar(),
body: Column(
children: <Widget>[
headerWidget(),
Container(
child: futureBuilder(),
)
],
),
);
}
#override
void setState(fn) {
super.setState(fn);
}
}
Future<List<Project>> getData() async {
String getProjects = "https://api.myjson.com/bins/1g3xpe";
final response = await http.get(getProjects);
if (response.statusCode == 200) {
List responseJson = json.decode(response.body);
return responseJson.map((m) => Project.fromJson(m)).toList();
} else
throw Exception(response.toString());
}
Widget futureBuilder() {
FutureBuilder<List<Project>>(
future: getData(),
builder: (context, snapshot) {
debugPrint(snapshot.hasData.toString());
},
);
return Center(
child: CircularProgressIndicator(),
);
}
Widget listWidget(List<Project> data) {
return ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
listItem(data.elementAt(index));
},
);
}
Widget listItem(Project project) {
return Card(
elevation: 6.0,
child: Column(
children: <Widget>[Text(project.projectId), Text(project.projectName)],
),
);
}
class EmptyAppBar extends StatelessWidget implements PreferredSizeWidget {
#override
Widget build(BuildContext context) {
return Container();
}
#override
Size get preferredSize => Size(0.0, 0.0);
}
UPDATE:
This is the code in builder part which I removed to debug.
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return new Text('loading...');
default:
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
else
return listWidget(snapshot.data);
}
You need to return your FutureBuilder so its part of the widget tree:
Widget futureBuilder() {
return FutureBuilder<List<Project>>(
future: getData(),
builder: (context, snapshot) {
// ...
},
);
}
Simply creating an instance of it wouldn't resolve the future.