<asynchronous suspension> in flutter - flutter

Im a newbie to flutter, while trying local notification with flutter to notify with data fetched from the API, i faced a problem where i could fetch the data but couldnot get the notification.
******BTW the code is not complete but it would should atleast work
this is my main.dart
import 'dart:async';
import 'dart:convert';
import 'notification_api.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Album> fetchAlbum() async {
final response = await http
.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/2'));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
class Album {
final int userId;
final int id;
final String title;
Album({
required this.userId,
required this.id,
required this.title,
});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late Future<Album> futureAlbum;
#override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Fetch Data Example'),
),
body: Center(
child: Column(
children: [
FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.title);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
),
ElevatedButton(
onPressed: (){
NotificationApi.showNotification(
title: 'hello',
body: 'sup',
);
},
child: const Text("Local Notification"),
),
],
),
),
),
);
}
}
this is my notification_api.dart
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class NotificationApi{
static final _notifications = FlutterLocalNotificationsPlugin();
static Future _notificationDetails() async{
return const NotificationDetails(
android: AndroidNotificationDetails(
'channel id',
'channel name',
channelDescription: 'channel description',
importance: Importance.max
),
iOS: IOSNotificationDetails(),
);
}
static Future showNotification({
int id = 0,
String? title,
String? body,
String? payload,
}) async =>
_notifications.show(
id,
title,
body,
await _notificationDetails(),
payload: payload,
);
}
here is the error
E/flutter (23893): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: MissingPluginException(No implementation found for method show on channel dexterous.com/flutter/local_notifications)
E/flutter (23893): #0 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:154:7)
E/flutter (23893): <asynchronous suspension>
E/flutter (23893): #1 FlutterLocalNotificationsPlugin.show (package:flutter_local_notifications/src/flutter_local_notifications_plugin.dart:194:7)
E/flutter (23893): <asynchronous suspension>
E/flutter (23893):

if you use like await word . erase all of them. you will see the emulator work again.

Related

How to do HTTP GET on an API link that contains nested array in Dart/Flutter?

I am trying to create an app that shows the current data in my web server using the Flutter plugin. I want to access the details of my web server using HTTP GET.
I followed this guide (https://docs.flutter.dev/cookbook/networking/fetch-data) and used the code as well but I can't make it work on my HTTP GET link since it has nested parameters.
Here is the code from the guide:
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Album> fetchAlbum() async {
final response = await http
.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
class Album {
final int userId;
final int id;
final String title;
const Album({
required this.userId,
required this.id,
required this.title,
});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['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;
#override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.title);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
),
),
),
);
}
}
Here is my link by the way: https://api.thingspeak.com/channels/1864145/fields/1.json?results=1
and this is the array it contains
{
"channel":{
"id":1864145,
"name":"channel name",
"description":"description",
"latitude":"0.0",
"longitude":"0.0",
"field1":"first field",
"field2":"second field",
"field3":"third field",
"field4":"fourth field",
"created_at":"2022-09-18T11:53:33Z",
"updated_at":"2022-09-18T12:26:54Z",
"last_entry_id":10742
},
"feeds":[
{"created_at":"2022-12-27T05:34:12Z",
"entry_id":10741,
"field1":"111",
"field2":"4",
"field3":"28",
"field4":"61"
}
]
}
///edit
For those confused, I don't have a code yet since I have no idea how to do it but I tried to replace the variables based on our own parameters and it did not work so I thought there is a different way/code for the nested array parameters.

Flutter http.get is not responding

I used exactly same code from Flutter Documentation to fetch data from internet.
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Album> fetchAlbum() async {
final response = await http
.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
class Album {
final int userId;
final int id;
final String title;
const Album({
required this.userId,
required this.id,
required this.title,
});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['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;
#override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.title);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
),
),
),
);
}
}
It should display data with text widget after getting data, but http.get is not returning and it just keeps showing CircularProgressIndicator like:
I'm using Windows 10, Flutter 3.7.0 stable channel, Dart 2.19.0, http 0.13.5 version. How to fix this?
This could be because you haven't added the interne permissions in you android manifest
<manifest...
...
<uses-permission android:name="android.permission.INTERNET"/>
....
/manifest>

Unhandled Exception: No ScaffoldMessenger widget found

I'm attempting to enable auto-login by obtaining a token saved in the local mobile instance. I'm encountering this error. I'm using Provider for state management.
In my main file , I want to retrieve my token to check whether user has been logged in or not. What i'm, doing wrong ?
My Error
E/flutter ( 6547): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: No ScaffoldMessenger widget found.
E/flutter ( 6547): MyApp widgets require a ScaffoldMessenger widget ancestor.
E/flutter ( 6547): The specific widget that could not find a ScaffoldMessenger ancestor was:
E/flutter ( 6547): MyApp
E/flutter ( 6547): The ancestors of this widget were:
E/flutter ( 6547): _InheritedProviderScope<UserProvider?>
E/flutter ( 6547): ChangeNotifierProvider<UserProvider>
E/flutter ( 6547): _NestedHook
E/flutter ( 6547): MultiProvider
E/flutter ( 6547): [root]
E/flutter ( 6547): Typically, the ScaffoldMessenger widget is introduced by the MaterialApp at the top of your application widget tree.
Main.dart file
void main() {
runApp(MultiProvider(
providers: [ChangeNotifierProvider(create: (context) => UserProvider())],
child: const MyApp()));
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}):super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final AuthService authService = AuthService();
#override
void initState() {
// TODO: implement initState
authService.getUserData(context);
super.initState();
}
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ShopOne',
theme: ThemeData(
scaffoldBackgroundColor: GlobalVariables.backgroundColor,
textTheme: Theme.of(context)
.textTheme
.apply(bodyColor: Colors.white, displayColor: Colors.white),
colorScheme:
const ColorScheme.light(primary: GlobalVariables.secondaryColor),
appBarTheme: const AppBarTheme(
elevation: 0, iconTheme: IconThemeData(color: Colors.white)),
//
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
onGenerateRoute: ((settings) => generateRoute(settings)),
home: Provider.of<UserProvider>(context).user.token.isNotEmpty
? const HomeScreen()
: const AuthScreen()
);
if (Provider.of<UserProvider>(context).user.token.isNotEmpty) {
print('true');
} else {
print('false');
;
}
}
}
get user function
void getUserData(
BuildContext context,
) async {
try {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? token = prefs.getString("auth-token");
if (token == null) {
prefs.setString('auth-token', '');
}
var tokenRes = await http.post(Uri.parse('$uri//tokenisvalid'),
headers: <String, String>{
'Content-Type': 'application/json;charset=UTF-8',
'auth-token': token!
});
print('tokenres working 1');
var response = jsonDecode(tokenRes.body);
if (response == true) {
http.Response userRes = await http.get(Uri.parse('$uri/'),
headers: <String, String>{
'Content-type': 'application/json;charset=UTF-8',
'auth-token': token
});
print('tokenres working 2');
var userProvider = Provider.of<UserProvider>(context, listen: false);
userProvider.setUser(userRes.body);
}
} catch (e) {
showSnackBar(context, e.toString());
}
}
This happened because the context that you used in showSnackBar, doesn't belongs to any Scaffold, you need to separate the MaterialApp's home to new StatefulWidget class like this:
class ScreenManager extends StatefulWidget {
const ScreenManager({Key? key}) : super(key: key);
#override
State<ScreenManager> createState() => _ScreenManagerState();
}
class _ScreenManagerState extends State<ScreenManager> {
#override
void initState() {
// TODO: implement initState
authService.getUserData(context);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Provider.of<UserProvider>(context).user.token.isNotEmpty
? const HomeScreen()
: const AuthScreen(),
);
}
void getUserData(BuildContext context) async {
try {
...
} catch (e) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showSnackBar(context, e.toString());
});
}
}
}
then use it like this:
home: ScreenManager();
Note: don't forget to remove authService.getUserData(context) from _MyAppState's initState.

How to Pass parameters in url. in flutter

I want to pass some parameters in flutter url
here is example:
parameters are source , destination , type, fare.
url is http://localhost:9000/api/bmrc/fare/source/destination/fare/type
url need to send http://localhost:9000/api/bmrc/fare/1/17/2/SJT
(workinng in postman/thunderclient)
I tried after passing these parameters in body but its not worked for me
They are Path Parameters, not body JSON.
You can call your API like this:
callAPI(int source,int destination,int fare,String type){
String _url= "http://localhost:9000/api/bmrc/fare/${source}/${destination}/${fare}/${type}";
... //call your API with _url
}
You can change the param types as you like
its a POST Call:
To get data from from your url, try this.
Here is the sample Example:
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<Post> post;
#override
void initState() {
super.initState();
post = fetchPost();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter REST API Example',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: Scaffold(
appBar: AppBar(
title: Text('Flutter REST API Example'),
),
body: Center(
child: FutureBuilder<Post>(
future: post,
builder: (context, abc) {
if (abc.hasData) {
return Text(abc.data.title);
} else if (abc.hasError) {
return Text("${abc.error}");
}
// By default, it show a loading spinner.
return CircularProgressIndicator();
},
),
),
),
);
}
}
Future<Post> fetchPost() async {
final response = await http.get('Give your JSON file web link.');
if (response.statusCode == 200) {
// If the call to the server was successful (returns OK), parse the JSON.
return Post.fromJson(json.decode(response.body));
} else {
// If that call was not successful (response was unexpected), it throw an error.
throw Exception('Failed to load post');
}
}
class Post {
final int userId;
final int id;
final String title;
final String description;
Post({this.userId, this.id, this.title, this. description});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
userId: json['userId'],
id: json['id'],
title: json['title'],
description: json[' description'],
);
}
}

Does stream builder build my widgets again and again without any change in stream?

Stream builder in Flutter is getting recalled. I am not sure why. I believe the problem might be that i have a bloc provider in stream builder. My stream dataBloc.dataStream is not changing, to cause the streambuilder to build again. Not sure, what i am doing wrong. Does stream builder build my widgets again and again without any change in stream. Obviously that's not true! Right?
Widget build(context) {
final DataBloc dataBloc = DataBlocProvider.of(context);
print("dropdown build called again");
// this doesn't print recursively so this is perfect.
// So my build is not getting called again.
return StreamBuilder(
stream: dataBloc.dataStream,
builder: (context, snapshot) {
//ToDo remove prints
print("dropdown ${snapshot.data}");
// there is no change in snapshot.data, however print is getting called recursively. This is bad and wrong
// so my stream builder is getting called again, and this is wrong
String key = dataElement.conditionalField;
String _valueArray = dataElement.conditionalValues.toString();
String conditionalValue =
_valueArray.substring(1, _valueArray.length - 1);
Map<String, String> dataMap = snapshot.hasData ? snapshot.data : {};
bool isVisible = true;
if (key != "" &&
dataMap.containsKey(key) &&
dataMap[key] == conditionalValue.toString()) {
isVisible = true;
} else if (key != "") {
isVisible = false;
}
return Visibility(
child: BlocDropDownProvider(
fieldName: dataElement.key,
dataBloc: dataBloc,
child: Card(
color: Colors.grey[100],
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
label,
new Container(
height: 8.0,
),
dropDown,
],
),
),
),
visible: isVisible? true:false,
);
output on console is :
I/flutter (14422): dropdown {idnumber: 10}
I/flutter (14422): dropdown {idnumber: 10}
I can't really replicate this issue with 1:1 accuracy base from the given details. What I encountered similarly is that build within StreamBuilder is being called again on ConnectionState changes.
Here's a minimal repro using StreamBuilder sending a HTTP request. The HTTP request sample here is based from this Flutter Networking guide.
import 'dart:async';
import 'package:flutter/material.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: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final streamController = StreamController();
#override
void initState() {
super.initState();
fetchAlbum().then((response) => streamController.add(response));
}
#override
void dispose() {
super.dispose();
streamController.close();
}
#override
Widget build(BuildContext context) {
debugPrint('build');
return StreamBuilder(
stream: streamController.stream,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
debugPrint('Stream $snapshot');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: snapshot.hasData
? Text('Album ${snapshot.data.title}')
: Text('Waiting...'),
),
);
},
);
}
Future<Album> fetchAlbum() async {
final response =
await http.get('https://jsonplaceholder.typicode.com/albums/1');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
}
class Album {
final int userId;
final int id;
final String title;
Album({this.userId, this.id, this.title});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
Then again, I suggest not being too concerned with build costs as long as the Widgets inside the build is manageable. More details about Flutter best practices are discussed in this doc.