How to complete async operation in initState and update ui? - flutter

The longRequest() completes with a code 200, but ui displays 400 when the longRequest() is completed. What's wrong with this scenario?
class Parsit extends StatefulWidget {
#override
_ParsitState createState() => _ParsitState();
}
class _ParsitState extends State<Parsit> {
int code = 400;
#override
void initState() {
super.initState();
setState(() {
longRequest().then((value) => code = value);
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Center(
child: Text('$code'),
),
);
}
Future<int> longRequest() async {
final response = await Requests.get('http://google.com');
response.raiseForStatus();
...
print(response.statusCode); // 200
return response.statusCode;
}
}

you can with a boolean value check longRequest() is finished and for UI put a loading or something :
class Parsit extends StatefulWidget {
#override
_ParsitState createState() => _ParsitState();
}
class _ParsitState extends State<Parsit> {
int code = 400;
bool longRequestIsFinish = false
#override
void initState() {
super.initState();
longRequest().then((value) {
setState(() {
code = value;
longRequestIsFinish = true;
});
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Center(
child: longRequestIsFinish ? Text('$code') : LoadingWidget(),
),
);
}
Future<int> longRequest() async {
final response = await Requests.get('http://google.com');
response.raiseForStatus();
...
print(response.statusCode); // 200
return response.statusCode;
}
}

using FutureBuilder widget
class Parsit extends StatefulWidget {
#override
_ParsitState createState() => _ParsitState();
}
class _ParsitState extends State<Parsit> {
Future<int> code;
#override
void initState() {
super.initState();
code = longRequest();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Center(
child: FutureBuilder<int>(
future: code,
builder: (context, snaopshot) {
if (snapshot.hasData)
return Text(snapshot.data.toString());
return CircularProgressIndicator();
),
);
}
Future<int> longRequest() async {
final response = await Requests.get('http://google.com');
response.raiseForStatus();
...
print(response.statusCode); // 200
return response.statusCode;
}
}

Related

flutter does not show data from API

When I try to pull data from the jsonplaceholder API and put it on the screen, I don't have any problems, but when I try to change the data in this link (https://reqres.in/api/users?page=2 ) on the reqres.io site with the same codes, only by changing the API and model, the text no data appears on the screen. I'm getting it can you help me ?
My project with JsonPlaceHolder
main.dart
import 'package:flutter/material.dart';
import 'package:my_app/models/json_model.dart';
import 'service/api_service.dart';
void main() =\> runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: const Text('Json Deneme'),
),
body: const Home(),
),
);
}
}
class Home extends StatefulWidget {
const Home({super.key});
#override
State\<Home\> createState() =\> \_HomeState();
}
class \_HomeState extends State\<Home\> {
List\<JsonModel\>? \_postItems;
bool \_isLoading = false;
String? \_errorMessage;
#override
void initState() {
super.initState();
loadData();
}
Future\<void\> loadData() async {
setState(() {
_isLoading = true;
_errorMessage = null;
});
try {
final postItems = await Api.fetchApi();
setState(() {
_postItems = postItems;
_isLoading = false;
});
} catch (e) {
setState(() {
_isLoading = false;
_errorMessage = 'Error fetching data: $e';
});
}
}
#override
Widget build(BuildContext context) {
if (\_isLoading) {
return const Center(child: CircularProgressIndicator());
} else if (\_postItems == null || \_postItems!.isEmpty) {
return const Center(child: Text('No Data'));
} else {
return ListView.builder(
itemCount: \_postItems!.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(\_postItems!\[index\].name),
);
},
);
}
}
}`
api_service.dart
`import 'dart:io';
import 'package:my_app/models/json_model.dart';
import 'package:dio/dio.dart';
class Api {
static Future<List<JsonModel>?> fetchApi() async {
final res = await Dio().get("https://jsonplaceholder.typicode.com/users");
if (res.statusCode == HttpStatus.ok) {
final data = res.data!;
if (data is List) {
return data.map((e) =\> JsonModel.fromMap(e)).toList();
}
}
return <JsonModel>[];
}
}
`
conclusion
conclusion
My project with reqres.in
main.dart
`import 'package:flutter/material.dart';
import 'package:my_app/models/json_model.dart';
import 'service/api_service.dart';
void main() =\> runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: const Text('Json Deneme'),
),
body: const Home(),
),
);
}
}
class Home extends StatefulWidget {
const Home({super.key});
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<ReqresModel>? _postItems;
bool _isLoading = false;
String? _errorMessage;
#override
void initState() {
super.initState();
loadData();
}
Future<void> loadData() async {
setState(() {
_isLoading = true;
_errorMessage = null;
});
try {
final postItems = await Api.fetchApi();
setState(() {
_postItems = postItems;
_isLoading = false;
});
} catch (e) {
setState(() {
_isLoading = false;
_errorMessage = 'Error fetching data: $e';
});
}
}
#override
Widget build(BuildContext context) {
if (_isLoading) {
return const Center(child: CircularProgressIndicator());
} else if (_postItems == null || _postItems!.isEmpty) {
return const Center(child: Text('No Data'));
} else {
return ListView.builder(
itemCount: _postItems!.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(\_postItems!\[index\].data\[index\].firstName),
);
},
);
}
}
}`
api_servise.dart
`import 'dart:io';
import 'package:my_app/models/json_model.dart';
import 'package:dio/dio.dart';
class Api {
static Future<List<ReqresModel>?> fetchApi() async {
final res = await Dio().get("https://reqres.in/api/users?page=2");
if (res.statusCode == HttpStatus.ok) {
final data = res.data!;
if (data is List) {
return data.map((e) => ReqresModel.fromMap(e)).toList();
}
}
return <ReqresModel>[];
}
}`
conclusion
conclusion
**Thank you **
I sent the API using Postman, there was no problem, I don't think the problem was caused by the API, sorry for my bad English
Change your api_service.dart for reqres.in project with following code.
First api which returns List directly, but second api which return Json which has data as List
Edited
main.dart
import 'package:flutter/material.dart';
import 'package:my_app/models/json_model.dart';
import 'service/api_service.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: const Text('Json Deneme'),
),
body: const Home(),
),
);
}
}
class Home extends StatefulWidget {
const Home({super.key});
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<ReqresModel>? _postItems;
bool _isLoading = false;
String? _errorMessage;
#override
void initState() {
super.initState();
loadData();
}
Future<void> loadData() async {
setState(() {
_isLoading = true;
_errorMessage = null;
});
try {
final postItems = await Api.fetchApi();
setState(() {
_postItems = postItems;
_isLoading = false;
});
} catch (e) {
setState(() {
_isLoading = false;
_errorMessage = 'Error fetching data: $e';
});
}
}
#override
Widget build(BuildContext context) {
if (_isLoading) {
return const Center(child: CircularProgressIndicator());
} else if (_postItems == null || _postItems!.isEmpty) {
return const Center(child: Text('No Data'));
} else {
return ListView.builder(
itemCount: _postItems!.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_postItems![index].firstName),//Updated here
);
},
);
}
}
}
api_service.dart
import 'dart:io';
import 'package:my_app/models/json_model.dart';
import 'package:dio/dio.dart';
class Api {
static Future<List<ReqresModel>?> fetchApi() async {
final res = await Dio().get("https://reqres.in/api/users?page=2");
if (res.statusCode == HttpStatus.ok) {
final data = res.data!['data'];//Updated here
if (data is List) {
return data.map((e) => ReqresModel.fromMap(e)).toList();
}
}
return <ReqresModel>[];
}
}
ReqresModel - should be updated
//Updated here
class ReqresModel {
int? id;
String? email;
String? firstName;
String? lastName;
String? avatar;
ReqresModel(
{this.id, this.email, this.firstName, this.lastName, this.avatar});
ReqresModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
email = json['email'];
firstName = json['first_name'];
lastName = json['last_name'];
avatar = json['avatar'];
}
}

Flutter camera error LateInitializationError: Field 'cameraController' has not been initialized

I'm trying to write a code to use the camera with Flutter, but even by following the steps seen online it cannot initialize cameraController.
Here is my code :
class CameraPage extends StatefulWidget {
const CameraPage({Key? key}) : super(key: key);
#override
State<CameraPage> createState() => _CameraPageState();
}
class _CameraPageState extends State<CameraPage> {
late List<CameraDescription> cameras;
late CameraController cameraController;
#override
void initState() {
startCamera();
super.initState();
}
void startCamera() async {
cameras = await availableCameras();
cameraController = CameraController(
cameras[0],
ResolutionPreset.high
);
print(" Camera controller : $cameraController");
cameraController.initialize().then((value) {
if(!mounted) {
return;
}
setState(() {}); //To refresh widget
}).catchError((e) {
print(e);
});
}
#override
void dispose() {
cameraController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
if(cameraController.value.isInitialized) {
return Scaffold(
body: Stack(
children: [
CameraPreview(cameraController),
],
),
);
} else {
return SizedBox();
}
}
}
The print(" Camera controller : $cameraController"); is working fine and returns me a camera controller, so it might be initialized at some point ?
I will suggest using FutureBuilder to handle future method.
class _CameraPageState extends State<CameraPage> {
late List<CameraDescription> cameras;
late CameraController cameraController;
#override
void initState() {
super.initState();
}
Future<void> startCamera() async {
cameras = await availableCameras();
cameraController = CameraController(cameras[0], ResolutionPreset.high);
}
late final cameraInit = Future.wait([
startCamera(),
cameraController.initialize(),
]);
#override
void dispose() {
cameraController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
FutureBuilder(
future: cameraInit,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return CameraPreview(cameraController);
} else {
return const Center(child: CircularProgressIndicator());
}
},
),
],
),
);
}
}
Also check this cookbook.
Your problem is that in the first build your camera controller is not initialized yet, because initialization is in an async method. You can use a Boolean flag to track is initialized, before accessing the late property.
bool _cameraInitialized = false;
void startCamera() async {
cameras = await availableCameras();
cameraController = CameraController(cameras[0], ResolutionPreset.high);
print(" Camera controller : $cameraController");
cameraController.initialize().then((value) {
if (!mounted) {
return;
}
setState(() {
_cameraInitialized = true; // updating the flag after camera is initialized
}); //To refresh widget
}).catchError((e) {
print(e);
});
}
#override
Widget build(BuildContext context) {
if (_cameraInitialized && cameraController.value.isInitialized) {
return Scaffold(
body: Stack(
children: [
CameraPreview(cameraController),
],
),
);
} else {
return SizedBox();
}
}

How to send the fetched data everytime to some other widget in Flutter

Wanted to pass the updated values of fetchedEntriesInApp to PasswdList widget everytime it loads.
Below is my code.
main.dart
Future fetchEntries() async {
var fetchedEntries = [];
var db = FirebaseFirestore.instance;
final res = await db.collection("password_entries").get().then((event) {
for (var doc in event.docs) {
var resDic = {
"entry_id": doc.id,
"data": doc.data(),
};
fetchedEntries.add(resDic);
}
});
return fetchedEntries;
}
class Body extends StatefulWidget {
#override
State<Body> createState() => _BodyState();
}
class _BodyState extends State<Body> {
late Future fetchedEntriesInApp;
#override
void initState() {
super.initState();
fetchedEntriesInApp = fetchEntries();
}
void refreshEntries() {
setState(() {
fetchedEntriesInApp = fetchEntries();
});
}
#override
Widget build(BuildContext context) {
setState(() {});
return FutureBuilder(
future: fetchedEntriesInApp!,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text('Loading');
}
return Column(children: [
PasswdList(fetchedEntriesInApp),
RaisedButton(
onPressed: () {
Navigator.pushNamed(
context,
'/addPasswd',
arguments: AddPasswd(fetchEntries),
);
},
child: Text('Add Psswd'),
),
]);
});
}
}
PasswdList Widget
class PasswdList extends StatefulWidget {
var abc;
PasswdList(this.abc);
#override
State<PasswdList> createState() => _PasswdListState();
}
class _PasswdListState extends State<PasswdList> {
var fetchedEntriesInApp;
#override
Widget build(BuildContext context) {
var entries;
setState(() {
entries = widget.abc;
});
print(entries);
return Container(
height: 500,
child: ListView(
children: [
PasswdCard(),
],
),
);
}
}
You can add one variable for password list in your password list widget like,
class PasswdList extends StatefulWidget {
var passwordlist;
PasswdList(this.passwordlist);
#override
State<PasswdList> createState() => _PasswdListState();
}
class _PasswdListState extends State<PasswdList> {
var fetchedEntriesInApp;
#override
Widget build(BuildContext context) {
var entries;
setState(() {
entries = widget.passwordlist;
});
print(entries);
return Container(
height: 500,
child: ListView(
children: [
PasswdCard(),
],
),
);
}
}
And you can pass it to the navigator like,
RaisedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>PasswdList (fetchedEntriesInApp.values,
),
);
},
Since your PasswdList is a Stateful widget and it is embedded in your view, you can use the callback
#override
void didUpdateWidget(covariant PasswdList oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.abc != oldWidget.abc)
setState(() {
//You can have a var in your state class and re-assign it to the new value
});
}
Note: in order for this to work, you need to re-initialize the abc list and pass it to your widget, otherwise you might need to change the if statement condition

Async function in flutter does not return null

I am trying to check the login of the user. But, checklogin() even on returning null doesn't equate to null in the if condition.
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
if(checkLogin() == null) {
return Login();
} else {
return Dashboard();
}
}
Future<String> checkLogin() async {
var prefs = await SharedPreferences.getInstance();
var key = 'Token';
var value = prefs.getString(key);
print(value);
return value;
}
}
I just used a future builder which manages the data returned from future through AsyncSnapshot.
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: checkLogin(),
builder: (BuildContext context, AsyncSnapshot snapshot){
if (snapshot.hasData){
var value = snapshot.data;
if(value == null){
return Login();
}else{
return Dashboard();
}
}else return Dashboard();
}
);
}
Future<String> checkLogin() async {
var prefs = await SharedPreferences.getInstance();
var key = 'Token';
var value = prefs.getString(key);
print(value);
return value;
}
}
The issue is checkLogin function is async and return Future for which you'll have wait and you wait in build directly.
So, here is a better and correct implementation.
``
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool isLogin = false;
#override
void initState() {
checkLogin();
super.initState();
}
checkLogin() async {
var prefs = await SharedPreferences.getInstance();
var key = 'Token';
var value = prefs.getString(key) ?? false;
setState(() {isLogin = value;});
}
#override
Widget build(BuildContext context) {
return isLogin ? Login() : Dashboard();
}
}
``

Flutter avoid multiple running FutureBuilder

in my simple code as new screen, unfortunately FutureBuilder work and get data from method twice!!
i'm not sure whats problem and how can i avoid that
class LessonDetail extends StatefulWidget {
final String monthKey;
final String lessonFileKey;
LessonDetail({#required this.monthKey, #required this.lessonFileKey});
#override
State<StatefulWidget> createState() {
return _LessonDetailState(monthKey, lessonFileKey);
}
}
class _LessonDetailState extends BaseState<LessonDetail> {
String monthKey;
String lessonFileKey;
_LessonDetailState(this.monthKey, this.lessonFileKey);
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
body: FutureBuilder(
future: _getLessonDetail(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
PlayLessonResponse response = snapshot.data;
print(response);
}
return Center(
child: CircularProgressIndicator(),
);
}),
),
);
}
Future<PlayLessonResponse> _getLessonDetail() async {
AudioList audioList = AudioList(
'http://www.sample.com',
'aaaaa'
);
List<AudioList> lst = [audioList,audioList,audioList];
PlayLessonResponse response = PlayLessonResponse(
2,
'',
'http://www.sample.com',
'2',
lst,
1,
'ssss'
);
print('++++++++++++++++++++');
return response;
}
}
BaseState class content:
abstract class BaseState<T extends StatefulWidget> extends State {
final Connectivity _connectivity = Connectivity();
StreamSubscription<ConnectivityResult> _connectivitySubscription;
bool isOnline = true;
Future<void> initConnectivity() async {
try {
await _connectivity.checkConnectivity();
} on PlatformException catch (e) {
print(e.toString());
}
if (!mounted) {
return;
}
await _updateConnectionStatus().then((bool isConnected){
if(mounted){
setState(() {
isOnline = isConnected;
});
}
});
}
#override
void initState() {
super.initState();
initConnectivity();
_connectivitySubscription = Connectivity()
.onConnectivityChanged
.listen((ConnectivityResult result) async {
await _updateConnectionStatus().then((bool isConnected){
if(mounted){
setState(() {
isOnline = isConnected;
});
}
});
});
}
#override
void dispose() {
_connectivitySubscription.cancel();
super.dispose();
}
Future<bool> _updateConnectionStatus() async {
bool isConnected;
try {
final List<InternetAddress> result =
await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
isConnected = true;
}
} on SocketException catch (_) {
isConnected = false;
return false;
}
return isConnected;
}
}
output:
I/flutter (32289): ++++++++++++++++++++
I/flutter (32289): ++++++++++++++++++++
Just like what #Ricardo said, you shouldn't call the function directly inside the FutureBuilder's future method.
Instead, you should 1st run your function in init state, and store the response in a new variable. Only then assign variable to the future of FutureBuilder.
Code Example:
class LessonDetail extends StatefulWidget {
final String monthKey;
final String lessonFileKey;
LessonDetail({#required this.monthKey, #required this.lessonFileKey});
#override
State<StatefulWidget> createState() {
return _LessonDetailState(monthKey, lessonFileKey);
}
}
class _LessonDetailState extends BaseState<LessonDetail> {
String monthKey;
String lessonFileKey;
Future<PlayLesssonResponse> _myResponse; //added this line
_LessonDetailState(this.monthKey, this.lessonFileKey);
#override
void initState() {
_myResponse = _getLessonDetail(); // added this line
super.initState();
}
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
body: FutureBuilder(
future: _myResponse, //use _myResponse variable here
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
PlayLessonResponse response = snapshot.data;
print(response);
}
return Center(
child: CircularProgressIndicator(),
);
}),
),
);
}
Future<PlayLessonResponse> _getLessonDetail() async {
AudioList audioList = AudioList(
'http://www.sample.com',
'aaaaa'
);
List<AudioList> lst = [audioList,audioList,audioList];
PlayLessonResponse response = PlayLessonResponse(
2,
'',
'http://www.sample.com',
'2',
lst,
1,
'ssss'
);
print('++++++++++++++++++++');
return response;
}
}