I want to call my Remote Config Instance on Flutter App StartUp
I have set up Riverpod as follows
class ConfigService {
// Will Initialise here
final RemoteConfig _remoteConfig;
ConfigService(this._remoteConfig);
Future<void> initialise() async {
...// Will fetchAndActivate
}
final remoteConfigProvider = Provider<RemoteConfig>((ref) {
return RemoteConfig.instance;
});
final configProvider = Provider<ConfigService>((ref) {
final _config = ref.read(remoteConfigProvider);
return ConfigService(_config);
});
I would want to call it in the main after
...
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
... here
But this can't be done because one needs a Reader and the ProviderScope is below this level
How do I call this provider in my main ?
The short answer is you can't. What you should do is call the Provider within the ProviderScope.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends ConsumerWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, ScopedReader watch) {
final config = watch(configProvider);
return Container();
}
}
Related
My flutter app's default splash screen takes too much time when the app opens time,
And also added the flutter flavor's
screen record is
My folder is
my main_dev.dart code is
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await MobileAds.instance.initialize();
FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
_initConfig();
final fcmToken = await FirebaseMessaging.instance.getToken();
FirebaseMessaging.onMessageOpenedApp.listen(
(RemoteMessage message) => {print('onMessageOpenedApp $message')});
var configuredApp = AppConfig(
appDisplayName: "dev Config",
fcmToken: fcmToken!,
baseUrl: 'https://dev-services.ducoinsure.com/',
appInternalId: 1,
child: const MyHomepage(),
);
// HttpOverrides.global = MyHttpOverrides();
mainCommon();
runApp(configuredApp);
}
Future<void> _initConfig() async {
await _remoteConfig.setConfigSettings(RemoteConfigSettings(
fetchTimeout: const Duration(seconds: 1),
minimumFetchInterval: const Duration(seconds: 10),
));
_fetchConfig();
}
void _fetchConfig() async {
await _remoteConfig.fetchAndActivate();
}
my InheritedWidget is
import 'package:flutter/material.dart';
class AppConfig extends InheritedWidget {
const AppConfig(
{super.key,
required this.appDisplayName,
required this.appInternalId,
required Widget child,
required this.baseUrl,
required this.fcmToken})
: super(child: child);
final String appDisplayName, baseUrl, fcmToken;
final int appInternalId;
static AppConfig? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<AppConfig>();
}
#override
bool updateShouldNotify(InheritedWidget oldWidget) => false;
}
I am using flutter_native_splash package and shared_preferneces to store my app data. I have the following code in my main.dart file.
import 'package:flutter/material.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:location/location.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'home_management.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
FlutterNativeSplash.removeAfter(initialization);
runApp(const MyApp());
}
void initialization(BuildContext context) async {
// Initialise shared preferences
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
// Initialise user location and store it in shared preferences
Location _location = Location();
bool? _serviceEnabled;
PermissionStatus? _permissionGranted;
LocationData? _locationData;
_serviceEnabled = await _location.serviceEnabled();
if (!_serviceEnabled) {
_serviceEnabled = await _location.requestService();
}
_permissionGranted = await _location.hasPermission();
if (_permissionGranted == PermissionStatus.denied) {
_permissionGranted = await _location.requestPermission();
}
_locationData = await _location.getLocation();
sharedPreferences.setDouble('latitude', _locationData.latitude!);
sharedPreferences.setDouble('longitude', _locationData.longitude!);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(brightness: Brightness.light),
darkTheme: ThemeData(brightness: Brightness.dark),
themeMode: ThemeMode.dark,
home: const HomeManagement(),
);
}
}
I am using HomeManagement to manage my Pages with a bottom navigation bar, and the first page to load is RestaurantsMap() which looks as below.
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class RestaurantsMap extends StatefulWidget {
const RestaurantsMap({Key? key}) : super(key: key);
#override
State<RestaurantsMap> createState() => _RestaurantsMapState();
}
class _RestaurantsMapState extends State<RestaurantsMap> {
late Future<SharedPreferences> sharedPreferences;
#override
void initState() {
sharedPreferences = SharedPreferences.getInstance();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Restaurants Map'),
),
body: FutureBuilder(
future: sharedPreferences,
builder: (BuildContext context,
AsyncSnapshot<SharedPreferences> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
print(snapshot.data!.getDouble('latitude'));
return const Center(
child: Text('Start building something awesome! 💪🏻'),
);
} else {
return Container();
}
}),
);
}
}
Whenever I am accessing the latitude in RestaurantsMap inside the FutureBuilder, I am getting a null printed by the following line of code:
print(snapshot.data!.getDouble('latitude'));
Using print statements inside the initialization() function after sharedPreferences.setDouble returns the data, so the only logical explanation is that I am accessing the getDouble('latitude') before it is getting set.
Any observations/solutions would be helpful.
For future viewers, if anyone faces the same issue, just update to the latest version for flutter_native_splash. An update has been released that gives more flexibility to make a call to remove the splash screen.
Here is the new readme - https://pub.dev/packages/flutter_native_splash#3-set-up-app-initialization-optional
I need to get one stored value from shared preferences and put it into text widget. How can I do this without a future builder?
_currPage() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int page = prefs.getInt('currPage') ?? 0;
return page;
}
class _AllTasksPageState extends State<AllTasksPage> {
#override
Widget build(BuildContext context) {
...
Text(_currPage()); //not working
...
}
}
int page = 0;
#override
void initState() {
super.initState();
readData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('$page'),
),
);
}
void readData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
if (prefs.getInt('currPage') == null)
setState(() => page = 0);
else
setState(() => page = prefs.getInt('currPage')!);
}
create a helper class just for shared preferences
import 'package:shared_preferences/shared_preferences.dart';
class SPHelper {
SPHelper._();
static SPHelper sp = SPHelper._();
SharedPreferences? prefs;
Future<void> initSharedPreferences() async {
prefs = await SharedPreferences.getInstance();
}
Future<void> save(String name, String value) async {
await prefs!.setString(name, value);
}
String? get(String key) {
return prefs!.getString(key);
}
Future<bool> delete(String key) async {
return await prefs!.remove(key);
}
}
in your main function add
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await SPHelper.sp.initSharedPreferences();
...
runApp(MyApp());
...
}
then to get your data just write
SPHelper.sp.get("YOUR_KEY")
and to store your data just write
SPHelper.sp.save("YOUR_KEY","YOUR_VALUE")
This is the best way to use shared preference.
I hope that's will help you in your problem.
The simplest method is using a SharedPreferences provider:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(
MultiProvider(
providers: [
Provider.value(value: await SharedPreferences.getInstance()),
],
child: MaterialApp(
home: AllTasksPage(),
),
),
);
}
class AllTasksPage extends StatelessWidget {
const AllTasksPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final page = context.read<SharedPreferences>().getInt('currPage') ?? 0;
return Scaffold(body: Text('$page'));
}
}
If you don't want to use a future builder, the other solution is if you have a variable that tells you that are you still waiting/loading data and if yes, show a waiting screen:
class _AllTasksPageState extends State<AllTasksPage> {
bool _loading = true;
String? textValue; // String textValue = "";
#override
initState() {
super.initState();
setTextValue();
}
setTextValue() {
SharedPreferences prefs = await SharedPreferences.getInstance();
int page = prefs.getInt('currPage') ?? 0;
setState(() {
textValue = "$page";
_loading = false;
});
}
// then in the build method
#override
Widget build(BuildContext context) {
return _loading ? CircularProgressIndicator() : actualScreen();
}
}
This method will call the init function inside the stateless widget.
But how to emulate the call to dispose function?
var widget = StatelessWidgetExample();
await tester.pumpWidget(widget);
I also tried to emulate the removal from the tree.
await tester.pumpWidget(widget);
await tester.pumpWidget(Container());
but it didn't work
Did it like this
var key2 = Key('a');
var testStateful = _TestStateful(
key: key2,
child: TestInitDispose(),
);
await tester.pumpWidget(testStateful);
/// will call init
var state = tester.firstState<__TestStatefulState>(find.byKey(key2));
state.remove();
await tester.pump();
/// will call dispose
});
...
class _TestStateful extends StatefulWidget {
final Widget child;
const _TestStateful({Key? key, required this.child}) : super(key: key);
#override
__TestStatefulState createState() => __TestStatefulState();
}
class __TestStatefulState extends State<_TestStateful> {
bool showChild = true;
void remove() {
setState(() {
showChild = false;
});
}
#override
Widget build(BuildContext context) {
return showChild ? widget.child : Container();
}
}
You could use a StreamBuilder and replace YourWidget with another widget, then the dispose method for YourWidget is called.
void main() {
late StreamController<Widget> widgetStreamController;
setUp(() async {
widgetStreamController = StreamController<Widget>();
});
tearDown(() async {
await widgetStreamController.close();
});
Widget buildApp() {
return MaterialApp(
home: StreamBuilder<Widget>(
stream: widgetStreamController.stream,
builder: (context, snapshot) {
return snapshot.data ?? Container();
},
),
);
}
testWidgets('dispose widget', (tester) async {
await tester.pumpWidget(buildApp());
await tester.pumpAndSettle();
widgetStreamController.add(YourWidget());
await tester.pumpAndSettle();
// todo: check here if YourWidget is displayed
widgetStreamController.add(AnotherWidget());
await tester.pumpAndSettle();
// todo: check here if dispose was called
});
}
This worked for me and is relatively simple.
var widget = StatelessWidgetExample();
await tester.pumpWidget(widget);
await tester.pumpAndSettle();
await tester.pumpWidget(Container());
await tester.pumpAndSettle();
I am still pretty new to flutter and dart but I am currently trying to return a future widget that is my main game inside of a StatefulWidget and I am wondering if I need to use a future builder or if there is another way to do it?
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:untitled2/screens/game.dart';
class GamePlay extends StatefulWidget {
GamePlay({Key key}) : super(key: key);
#override
_GamePlayState createState() => _GamePlayState();
}
class _GamePlayState extends State<GamePlay> {
#override
Widget build(BuildContext context) async { // THIS IS WHERE IT SAYS THE PROBLEM IS
SharedPreferences storage = await SharedPreferences.getInstance();
MainGame mainGame = MainGame(storage);
return Scaffold(
body: mainGame.widget,
);
}
}
You can't use await inside the build method. You can use a FutureBuilder widget instead:
class _GamePlayState extends State<GamePlay> {
// Create a property variable to be updated
SharedPreferences _storage;
// Create a future to store the computation
Future<void> _future;
#override
void initState() {
super.initState();
// When creating the widget, initialize the future
// to a method that performs the computation
_future = _compute();
}
Future<void> _compute() async {
// Make your asynchronous computation here
_storage = await SharedPreferences.getInstance();
}
#override
Widget build(BuildContext context) async {
// Use a FutureBuilder to display to the user a progress
// indication while the computation is being done
return FutureBuilder(
future: _future,
builder: (context, snapshot) {
// If snapshot is not ready yet, display the progress indicator
if (snapshot.connectionState == ConnectionState.waiting)
return Center(child: CircularProgressIndicator());
// If it's ready, use the property
final SharedPreferences storage = _storage;
final MainGame mainGame = MainGame(storage);
return Scaffold(body: mainGame.widget);
},
);
}
}