Flutter application hangs when calling await rootBundle.loadString(...) from main() - flutter

I found this answer about storing global configuration into globals.dart.
How can I load configuration into it from assets/config.json?
I've tried like this:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:convert';
import 'globals.dart' as globals;
void main() async {
globals.config = jsonDecode(await rootBundle.loadString('assets/config.json'));
runApp(MyApp());
}
class MyApp extends StatelessWidget {
...
}
The application starts with a white screen. Nothings happens, no errors. I guess that await rootBundle.loadString(...) causes the application to hang.

You can copy paste run full code below
You need to add WidgetsFlutterBinding.ensureInitialized() in main()
without this line will produce white screen
globals.dart
Map<String, dynamic> config = {};
config.json
{
"id": "1",
"name": "abc"
}
code snippet
void main() async{
WidgetsFlutterBinding.ensureInitialized();
globals.config = jsonDecode(await rootBundle.loadString('assets/config.json'));
runApp(MyApp());
}
pubspec.yaml
assets:
- assets/
working demo
full code
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:convert';
import 'globals.dart' as globals;
void main() async{
WidgetsFlutterBinding.ensureInitialized();
globals.config = jsonDecode(await rootBundle.loadString('assets/config.json'));
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> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(globals.config["id"]),
Text(globals.config["name"]),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

Related

flutter - Variable that changed the value using sharedPreferences does not work properly -Has a simple example code

I created a button that allows users to change the size of letters.
I hope that the font size value will remain changed even if I run the app again.
But in my code, it goes back to its original state.I made a simple code of the problem I faced. I made three files.
1.HomePage.dart
import 'dart:async';
import 'dart:convert';
// import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import './main.dart';
import './count_page.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late CountPage _countPage;
#override
Widget build(BuildContext context) {
String prefsFont = "prefs_font";
_countPage = Provider.of<CountPage>(context, listen: true);
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.green,
title: Text('Test_Code'),
centerTitle: true, // 중앙 정렬
elevation: 0.0,
),
body: Container(
color: Colors.white,
child: Text('Font_size',style: TextStyle(fontSize: _countPage.font),),
),
bottomNavigationBar: BottomAppBar(
color: Colors.lime,
child: Container(
height: 200,
child: Row(
children: [
IconButton(
onPressed: () async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setDouble('prefs_font', 10);
setState(() {
_countPage.font = (prefs.getDouble('prefs_font') ?? 40) ;
});
},
icon: Icon(Icons.looks_one_outlined)),
IconButton(
onPressed: () async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setDouble('prefs_font', 40);
setState(() {
_countPage.font = (prefs.getDouble('prefs_font') ?? 40) ;
});
},
icon: Icon(Icons.looks_two_outlined)),
IconButton(
onPressed: () async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setDouble('prefs_font', 80);
setState(() {
_countPage.font = (prefs.getDouble('prefs_font') ?? 40) ;
});
},
icon: Icon(Icons.looks_3_outlined)),
],
),
),
),
);
}
}
2.main.dart
import 'dart:convert';
// import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import './HomePage.dart';
import 'count_page.dart';
void main() {
// WidgetsFlutterBinding.ensureInitialized();
// MobileAds.instance.initialize();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => CountPage(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
),
home: HomePage(),
),
);
}
}
3.count_page.dart
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class CountPage extends ChangeNotifier {
double _font = 40;
double get font => _font;
set font(double font) {
_font = font;
}
}
I made it small so that the composition is similar to the project I am working on.
The value changes even if you press the button, but it goes back to the beginning when you run the app again.
What is the problem?
So basically what's happening here is, you're only storing the font inside your SharedPrefrence. But you're not fetching it back when the app starts. Your CountPage class isn't storing the font size. It's shared preference that's storing it. So you have to just fetch the data from SharedPrefrence on the app start. And then use it in your code. A small example will be
Initialize SharedPrefrence so you can use later.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final sharedPref = await SharedPrefrence.getInstance(); // Instance will be created.
runApp(const MyApp());
}
2.Then make your class use SharedPrefrence instance by default
class CountPage extends ChangeNotifier {
final SharedPrefrence pref;
CountPage({required this.pref});
double get font => pref.getDouble('prefs_font') ?? 40;
setFont(double font) async {
await prefs.setDouble('prefs_font', font);
notifyListeners(); //This is necessary so that when the value changes the UI gets an update.
}
}
Then pass the SharedPrefrence instance to that class, so it can use it.
class MyApp extends StatelessWidget {
final SharedPrefrence pref;
const MyApp({Key? key, required this.pref}) : super(key: key); // Getting the instance from main.
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => CountPage({pref: pref}),//Passing the instance.
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
),
home: HomePage(),
),
);
}
}
Also on your save button it looks like you're using a getter to set the value, which shouldn't work. But now you can just call it like this
onPressed: () async {
setState(() { // Now this setState is optional bcz you're saying the notifier to notify it's listeners, whenever you're setting the value.
await _countPage.setFont(\* font size for this button */);
});
},

Flutter mocked clock (using withClock) behave inconsistenly when app starts with WidgetsFlutterBinding.ensureInitialized()

If flutter app starts with:
void main() {
WidgetsFlutterBinding.ensureInitialized();
Then clock.now() prints not mocked value if outside of build method.
Since I am using withClock in my tests, it makes my app unable to be properly tested as sometimes clock.now() returns mocked value, and sometimes real current time.
Please find the following example, and note that _printClockNow prints DateTime.now(), but it should print year 1990.
If you removed line WidgetsFlutterBinding.ensureInitialized() then it works as expected.
Minimum reproduceable code:
import 'package:clock/clock.dart';
import 'package:flutter/material.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
withClock(Clock.fixed(DateTime(1990)), () {
runApp(const MyApp());
});
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void _printClockNow() {
print('_printClockNow is: ${clock.now()}'); // prints real NOW
setState(() {});
}
#override
Widget build(BuildContext context) {
print('build clock.now() is: ${clock.now()}'); // prints mocked NOW
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[Dummy()],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _printClockNow,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class Dummy extends StatelessWidget {
#override
Widget build(Object context) {
return Text('t: ${clock.now()}');
}
}
Try with putting WidgetsFlutterBinding inside withClock.
void main() {
withClock(Clock.fixed(DateTime(1990)), () {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
});
}

How to write test for flutter package Flutter WeChat Assets Picker

I am trying to write a test on this flutter package https://pub.dev/packages/wechat_assets_picker using the Mocktail package https://pub.dev/packages/mocktail, but this package does not seem to have test in the documentation.
I have included the minimum reproducible example. The test file is currently not working, it is included as an example test code using the flutter Mocktail package.
It is supposed to mock AssetPicker.pickAssets to test whether it is actually called with the correct arguments. I am running in IOS simulator, we will need to add this key in ios/Runer/Info.plist, otherwise the simulator will close unexpectedly after clicking the add button.
main.dart
import 'package:flutter/material.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
final List<AssetEntity>? result =
await AssetPicker.pickAssets(context);
},
tooltip: 'Add photo',
child: const Icon(Icons.add),
),
);
}
}
widget_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:wechat/main.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
class _MockAssetPicker extends Mock implements AssetPicker {}
void main() {
testWidgets('It should call the WeChat asset picker',
(WidgetTester tester) async {
await tester.pumpWidget(const MyApp());
final BuildContext context = tester.element(find.byType(MyApp));
final assetPickerMock = _MockAssetPicker();
when(() => assetPickerMock.pickAssets(context)).thenAnswer((_) => Future.value([
const AssetEntity(
id: 'id1',
typeInt: 1,
width: 100,
height: 100,
),
]));
AssetPicker.instance = assetPickerMock;
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
verify(() => assetPickerMock.pickAssets(context)).called(1);
});
}
Mocking the picker is supported by the separated picker delegate: https://github.com/fluttercandies/flutter_wechat_assets_picker/pull/315
TL;DR, build your own delegate first, then set it through AssetPicker.setPickerDelegate(TestAssetPickerDelegate());

How can I play local mp3 on flutter web?

I try a lot packages, but no one can play a mp3 file from local file...
users can chiose a file from systerm
_myFile = await FilePickerCross.importFromStorage(
type: FileTypeCross.audio,
);
then , how can i play it on flutter web page ?
usually crash:
Error: NotSupportedError: Failed to load because no supported source was found.
at Object.createErrorWithStack (http://localhost:62567/dart_sdk.js:4352:12)
at Object._rethrow (http://localhost:62567/dart_sdk.js:38191:16)
at async._AsyncCallbackEntry.new.callback (http://localhost:62567/dart_sdk.js:38185:13)
at Object._microtaskLoop (http://localhost:62567/dart_sdk.js:38017:13)
at _startMicrotaskLoop (http://localhost:62567/dart_sdk.js:38023:13)
at http://localhost:62567/dart_sdk.js:33520:9
like this https://github.com/florent37/Flutter-AssetsAudioPlayer/issues/383
so can any way to play local mp3 file on flutter web?
You can use audioplayers package.
To play audio from local file :
AudioPlayer audioPlayer = AudioPlayer();
playLocal() async {
int result = await audioPlayer.play(localPath, isLocal: true);
}
For a detailed info please take a look at package documentation:
https://pub.dev/packages/audioplayers
You can copy paste run full code below
The following demo code has well tested on flutter web
you can use package https://pub.dev/packages/soundpool and call _soundpool.loadAndPlayUint8List(_myFile.toUint8List());
code snippet
Soundpool _soundpool;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
_soundpool = Soundpool();
...
FilePickerCross _myFile = await FilePickerCross.importFromStorage(
type: FileTypeCross.audio,
);
_soundpool.loadAndPlayUint8List(_myFile.toUint8List());
full code
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:file_picker_cross/file_picker_cross.dart';
import 'package:soundpool/soundpool.dart';
Soundpool _soundpool;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
_soundpool = Soundpool();
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> {
int _counter = 0;
void _incrementCounter() async {
FilePickerCross _myFile = await FilePickerCross.importFromStorage(
type: FileTypeCross.audio,
);
_soundpool.loadAndPlayUint8List(_myFile.toUint8List());
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

get local time for different location in flutter

is there a simple way to get time for a different location than the current location in flutter ?
for example the current location is set to Japan,Tokyo and I want the time in Turkey, Istanbul from the system itself not from API
You can copy paste run full code below
You can use package https://pub.dev/packages/timezone
Step 1: download 2019c.tzf from https://github.com/srawlins/timezone/tree/master/lib/data
Step 2: put 2019c.tzf to assets directory
Step 3: Edit pubspec.yaml
working demo
code snippet
void main() async {
WidgetsFlutterBinding.ensureInitialized();
var byteData = await rootBundle.load('assets/2019c.tzf');
initializeDatabase(byteData.buffer.asUint8List());
runApp(MyApp());
}
...
final detroit = getLocation('America/Detroit');
final us = getLocation('US/Pacific');
final tokyo = getLocation('Asia/Tokyo');
nowDetroit = new TZDateTime.now(detroit);
nowUs = new TZDateTime.now(us);
nowTokyo = TZDateTime.now(tokyo);
full code
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:timezone/timezone.dart';
import 'package:timezone/standalone.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
var byteData = await rootBundle.load('assets/2019c.tzf');
initializeDatabase(byteData.buffer.asUint8List());
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> {
int _counter = 0;
TZDateTime nowDetroit;
TZDateTime nowUs;
TZDateTime nowTokyo;
void _incrementCounter() {
final detroit = getLocation('America/Detroit');
final us = getLocation('US/Pacific');
final tokyo = getLocation('Asia/Tokyo');
nowDetroit = new TZDateTime.now(detroit);
nowUs = new TZDateTime.now(us);
nowTokyo = TZDateTime.now(tokyo);
_counter++;
setState(() {
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(' America/Detroit ${nowDetroit.toString()}'),
Text(' US/Pacific ${nowUs.toString()}'),
Text(' Asia/Tokyo ${nowTokyo.toString()}'),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}