I'm using Dio in my app and I want it to save cookies. It turns out I have to download three additional packages to do that: cookie_jar, dio_cookie_manager and path_provider.
I need to:
get application document directory:
Directory docDir = await getApplicationDocumentsDirectory();
pass it to PersistCookieJar
PersistCookieJar(storage: FileStorage(appDocDir.path + '/.cookies/'));
and add it to interceptors:
dio.interceptors.add(CookieManager(_cookieJar));
My problem is with the first part. For some reason, getApplicationDocumentsDirectory(); throws an error when not used inside a StatefulWidget widget.
I have two questions:
Why on earth would getting a path require a StatefulWidget?
How can I do what I want? I need to initialize Dio as early as possible so that by the time the UI loads, the user is already (or almost) logged in. Plus, I don't want to put the logic of initiating the client object inside the UI code, that might for example cause it to be initiated multiple times whenever the widget is created.
What my code looks like right now:
Future<void> initStuff() async {
initLocator();
Directory docDir = await getApplicationDocumentsDirectory(); // throws
print('docDir = ${docDir.path}');
Client cli = Client(docDir);
locator.registerSingleton<Client>(cli);
// ...
}
void main() async {
await initStuff();
runApp(MyApp());
}
Client constructor:
Client(Directory appDocDir) {
final _cookieJar = PersistCookieJar(storage: FileStorage(appDocDir.path + '/.cookies/'));
dio = Dio()
..options.baseUrl = Utils.SERVER_ADDRESS
..options.sendTimeout = 5000
..options.receiveTimeout = 5000
..options.connectTimeout = 5000
..interceptors.add(CookieManager(_cookieJar));
}
The error I'm getting:
E/flutter (24953): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: Null check operator used on a null value
E/flutter (24953): #0 MethodChannel.binaryMessenger
package:flutter/…/services/platform_channel.dart:142
E/flutter (24953): #1 MethodChannel._invokeMethod
package:flutter/…/services/platform_channel.dart:148
E/flutter (24953): #2 MethodChannel.invokeMethod
package:flutter/…/services/platform_channel.dart:331
E/flutter (24953): #3 MethodChannelPathProvider.getApplicationDocumentsPath
package:path_provider_platform_interface/src/method_channel_path_provider.dart:50
E/flutter (24953): #4 getApplicationDocumentsDirectory
package:path_provider/path_provider.dart:138
E/flutter (24953): #5 initStuff
package:proj/main.dart:20
E/flutter (24953): #6 main
package:proj/main.dart:63
E/flutter (24953): #7 _runMainZoned.<anonymous closure>.<anonymous closure> (dart:ui/hooks.dart:142:25)
E/flutter (24953): #8 _rootRun (dart:async/zone.dart:1354:13)
E/flutter (24953): #9 _CustomZone.run (dart:async/zone.dart:1258:19)
E/flutter (24953): #10 _runZoned (dart:async/zone.dart:1789:10)
E/flutter (24953): #11 runZonedGuarded (dart:async/zone.dart:1777:12)
E/flutter (24953): #12 _runMainZoned.<anonymous closure> (dart:ui/hooks.dart:138:5)
E/flutter (24953): #13 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:283:19)
E/flutter (24953): #14 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
OK: Thank you for updating your post.
The error is: Unhandled Exception: Null check operator used on a null value
It's coming from the 3rd party library call getApplicationDocumentsDirectory(), and it's occurring because you're calling a (currently uninitialized) value with null-safety enabled (a Good Thing!).
SUGGESTIONS:
Using a StatefulWidget is a bit more work - but it makes sense here. It also happens to resolve the problem for you, correct?
You might try making docDir nullable (e.g. String? path). This is exactly what the getApplicationDocument page shows:
https://pub.dev/documentation/path_provider/latest/path_provider/getApplicationDocumentsDirectory.html
Future<Directory> getApplicationDocumentsDirectory() async {
final String? path = await _platform.getApplicationDocumentsPath();
if (path == null) {
throw MissingPlatformDirectoryException(
'Unable to get application documents directory');
}
return Directory(path);
}
Finally, check out these links:
https://stackoverflow.com/a/67990442/421195
https://stackoverflow.com/a/68876902/421195
Run this in main() before everything else:
WidgetsFlutterBinding.ensureInitialized();
Related
I have this code below which plays a sound every time I hit a button.
AudioPlayer player = AudioPlayer();
Future<void> playSound({required String soundStr}) async {
player.setAsset(soundStr);
player.play();
}
But whenever I hit the button fast, sometimes it will show this error. How can I eliminate this error? I tried adding player.stop(); but it still the same.
E/flutter ( 4312): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(abort, Loading interrupted, null, null)
E/flutter ( 4312): #0 AudioPlayer._setPlatformActive.checkInterruption (package:just_audio/just_audio.dart:1236:7)
E/flutter ( 4312): #1 AudioPlayer._setPlatformActive.setPlatform (package:just_audio/just_audio.dart:1347:11)
E/flutter ( 4312): <asynchronous suspension>
E/flutter ( 4312):
E/flutter ( 4312): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(abort, Loading interrupted, null, null)
E/flutter ( 4312):
Edit: I tried adding await.
Future<void> playSound({required String soundStr}) async {
await player.setAsset(soundStr);
await player.play();
}
If I hit the buttons fast, it will still have error. Though it is now different.
E/flutter (21654): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Connection aborted
E/flutter (21654): #0 AudioPlayer._load (package:just_audio/just_audio.dart:843:11)
E/flutter (21654): <asynchronous suspension>
E/flutter (21654): #1 AudioPlayer.load (package:just_audio/just_audio.dart:770:14)
E/flutter (21654): <asynchronous suspension>
E/flutter (21654): #2 AudioPlayer.setAudioSource (package:just_audio/just_audio.dart:745:18)
E/flutter (21654): <asynchronous suspension>
E/flutter (21654): #3 playSound (package:super_pinoy_quiz/common/generic_methods.dart:396:3)
E/flutter (21654): <asynchronous suspension>
E/flutter (21654):
I'm using this package: just_audio: ^0.9.29
Though I tried using different package but it also encounters the same error when hitting the button fast: audioplayers: ^1.1.1
Both setAsset and play are future method, you can await for setting assets
Future<void> playSound({required String soundStr}) async {
await player.setAsset(soundStr);
await player.play();
}
I am using just_audio package to play my base64 string which is a wav file that I received from my server. The app is being played from an android device API 30-ish. This is the error once the base64 is loaded:
E/flutter (31770): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: MissingPluginException(No implementation found for method disposeAllPlayers on channel com.ryanheise.just_audio.methods)
E/flutter (31770): #0 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:165:7)
E/flutter (31770): <asynchronous suspension>
E/flutter (31770): #1 MethodChannelJustAudio.disposeAllPlayers (package:just_audio_platform_interface/method_channel_just_audio.dart:29:10)
E/flutter (31770): <asynchronous suspension>
E/flutter (31770):
E/flutter (31770): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: MissingPluginException(No implementation found for method init on channel com.ryanheise.just_audio.methods)
E/flutter (31770): #0 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:165:7)
E/flutter (31770): <asynchronous suspension>
E/flutter (31770): #1 MethodChannelJustAudio.init (package:just_audio_platform_interface/method_channel_just_audio.dart:13:5)
E/flutter (31770): <asynchronous suspension>
E/flutter (31770): #2 AudioPlayer._setPlatformActive.setPlatform (package:just_audio/just_audio.dart:1330:13)
E/flutter (31770): <asynchronous suspension>
E/flutter (31770):
My custom base64 player which I follow from the instruction of the page:
class MyAudioPlayer extends StreamAudioSource {
MyAudioPlayer({this.bytes});
List<int> bytes;
#override
Future<StreamAudioResponse> request([int start, int end]) async {
final start = 0;
final end = bytes.length;
return StreamAudioResponse(
sourceLength: bytes.length,
contentLength: end - start,
offset: start,
stream: Stream.value(bytes.sublist(start, end)),
contentType: "audio/x-wav",
);
}
}
The file in which I played:
void playTTS(String content) async{
Dialogs.showLoadingDialog(context: context);
final List<int> base64 = await ApiService.textToSpeech(content);
final player = AudioPlayer();
await player.setAudioSource(MyAudioPlayer(bytes: base64));
await player.play();
Navigator.of(context, rootNavigator: true).pop();
listString.value = List.from(listString.value..add(ValueNotifier(content)));
}
I have set already set this to my androidmanifest.xml to true already.
android:usesCleartextTraffic="true"
Any help is appreciated.
Seems like I don't have to convert the base64 Wav file with a custom class like the tutorial did. According to the online converter, all I have to do was put data:audio/wav;base64,$base64 before the actual base64 string and uses the normal setUrl and it worked:
final String code = "data:audio/wav;base64," + json.decode(response.body)["base64"];
final player = AudioPlayer();
player.setUrl(code);
await player.play();
I have my flutter App integrated with firebase, everything was fine but after i integrate firebase i got error whenever I try to run my app.
My main method looks like this:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(child: MyApp());
}
Error:
E/flutter ( 7922): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception:
PlatformException(firebase_core, FirebaseApp name [DEFAULT] already exists!, null, null)
E/flutter ( 7922): #0 StandardMethodCodec.decodeEnvelope
(package:flutter/src/services/message_codecs.dart:607:7)
E/flutter ( 7922): #1 MethodChannel._invokeMethod
(package:flutter/src/services/platform_channel.dart:177:18)
E/flutter ( 7922): <asynchronous suspension>
E/flutter ( 7922): #2 MethodChannel.invokeMapMethod
(package:flutter/src/services/platform_channel.dart:377:43)
E/flutter ( 7922): <asynchronous suspension>
E/flutter ( 7922): #3 MethodChannelFirebase.initializeApp (package:firebase_core_platform_interface/src/method_channel/method_channel_firebase.dart:100:40)
E/flutter ( 7922): <asynchronous suspension>
E/flutter ( 7922): #4 Firebase.initializeApp
(package:firebase_core/src/firebase.dart:40:31)
E/flutter ( 7922): <asynchronous suspension>
E/flutter ( 7922): #5 main (package:zunada/main.dart:11:3)
E/flutter ( 7922): <asynchronous suspension>
E/flutter ( 7922):
I had similar issue when create firebase app by flavors using flutterfire CLI. I realized that flutterfire create apps with same name in different projects. Then I tried 2 ways and it works.
1st: manually change app name in each firebase project.
2nd: add param name when initialize such as in main_dev.dart
Example:
await Firebase.initializeApp(
name: "dev-project",
options: DefaultFirebaseOptions.currentPlatform);
I used the same name used in the firebase console as my project ID for consistency, keep in mind that you cannot add spaces to the name (only dashes -).
I am using rootBundle to load config in flutter(2.5.x), this is my code:
Future<void> main() async {
final jsonStr = await rootBundle.loadString("assets/static/emoji-flags.json", cache: false);
}
and I already config the path in pubspec.yaml like this:
flutter:
uses-material-design: true
assets:
- assets/translations/
- assets/static/
but this code running error like this:
E/flutter (14207): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: Null check operator used on a null value
E/flutter (14207): #0 PlatformAssetBundle.load (package:flutter/src/services/asset_bundle.dart:222:39)
E/flutter (14207): #1 AssetBundle.loadString (package:flutter/src/services/asset_bundle.dart:68:33)
E/flutter (14207): #2 CachingAssetBundle.loadString (package:flutter/src/services/asset_bundle.dart:166:18)
E/flutter (14207): #3 main (package:flutter_learn/main.dart:10:36)
E/flutter (14207): #4 _runMainZoned.<anonymous closure>.<anonymous closure> (dart:ui/hooks.dart:145:25)
E/flutter (14207): #5 _rootRun (dart:async/zone.dart:1428:13)
E/flutter (14207): #6 _CustomZone.run (dart:async/zone.dart:1328:19)
E/flutter (14207): #7 _runZoned (dart:async/zone.dart:1863:10)
E/flutter (14207): #8 runZonedGuarded (dart:async/zone.dart:1851:12)
E/flutter (14207): #9 _runMainZoned.<anonymous closure> (dart:ui/hooks.dart:141:5)
E/flutter (14207): #10 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:283:19)
E/flutter (14207): #11 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
E/flutter (14207):
what should I do to load this config success? Am I missing something?
This is my environment reader, I map my json file to the data model.
import 'package:flutter/services.dart';
static Future<void> initializeApp(String? env) async {
env = env ?? "development";
final configString = await rootBundle.loadString('assets/config/env.json');
final jsonMap = json.decode(configString);
_config = ConfigModel.fromMap(jsonMap[env]);
}
Here is a simple example of my problem:
import 'dart:async';
void main() {
try {
Timer(Duration(seconds: 1), () {
throw TimeoutException('1 second has expired');
});
} catch (o) {
print("caught ${o.runtimeType}");
}
}
I imagine the problem is that the timer finishes counting down after the try-catch block terminates, considering that the countdown is asynchronous and initializing the Timer was successful. How can I catch the exception without changing the callback function of the Timer?
In my specific case, I am using flutter_blue and having trouble with the async method BluetoothDevice#connect().
/// Establishes a connection to the Bluetooth Device.
Future<void> connect({
Duration? timeout,
bool autoConnect = true,
}) async {
var request = protos.ConnectRequest.create()
..remoteId = id.toString()
..androidAutoConnect = autoConnect;
Timer? timer;
if (timeout != null) {
timer = Timer(timeout, () {
disconnect();
throw TimeoutException('Failed to connect in time.', timeout);
});
}
await FlutterBlue.instance._channel
.invokeMethod('connect', request.writeToBuffer());
await state.firstWhere((s) => s == BluetoothDeviceState.connected);
timer?.cancel();
return;
}
I called the method like so:
try {
await (device as BluetoothDevice).connect(timeout: Duration(seconds: 1));
} catch (o) {
print("caught ${o.runtimeType}");
}
Considering I wait for BluetoothDevice#connect() with await and timer is cancelled upon a successful connection (at the end of the method) with timer?.cancel();, I do not know why the try-catch is not catching the following TimeoutException:
E/flutter ( 3710): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: TimeoutException after 0:00:01.000000: Failed to connect in time.
E/flutter ( 3710): #0 BluetoothDevice.connect.<anonymous closure> (package:flutter_blue/src/bluetooth_device.dart:33:9)
E/flutter ( 3710): #1 _rootRun (dart:async/zone.dart:1346:47)
E/flutter ( 3710): #2 _CustomZone.run (dart:async/zone.dart:1258:19)
E/flutter ( 3710): #3 _CustomZone.runGuarded (dart:async/zone.dart:1162:7)
E/flutter ( 3710): #4 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1202:23)
E/flutter ( 3710): #5 _rootRun (dart:async/zone.dart:1354:13)
E/flutter ( 3710): #6 _CustomZone.run (dart:async/zone.dart:1258:19)
E/flutter ( 3710): #7 _CustomZone.bindCallback.<anonymous closure> (dart:async/zone.dart:1186:23)
E/flutter ( 3710): #8 Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
E/flutter ( 3710): #9 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:395:19)
E/flutter ( 3710): #10 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:426:5)
E/flutter ( 3710): #11 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
E/flutter ( 3710):
I see that the stack trace starts at BluetoothDevice#connect(), but I am not sure what to do about it.
I also tried calling then((_) {}, (o) => print("caught ${o.runtimeType}")) on the Future<void> returned by BluetoothDevice#connect() and then waiting for it inside the try-catch, yet I had no success.
Any ideas?
That connect method from the package you're using is poorly written. You're not doing anything wrong, whoever wrote that code did. If you poke around the GitHub Issues sections of that repository you'll find quite a few issues and pull requests related to this problem like this and the issues/PR it links.
The code in a timer callback exists outside of the function where the timer is instantiated. It's impossible to directly catch an error thrown in a timer callback.
If you want a timeout, don't use the functionality provided by this package, use the native Dart timeout function.
try {
await (device as BluetoothDevice).connect().timeout(Duration(seconds: 1));
} catch (o) {
print("caught ${o.runtimeType}");
}