Riverpod Alternative To SetUpLocator - flutter

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 default splash screen is taking time to load

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;
}

FlutterNativeSplash.removeAfter(initialisation) renders next screen before initialisation completes

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

Get value from SharedPreferences without future builder

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();
}
}

How to simulate disposing widget in flutter test?

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();

Change to a subtype of 'Widget'. Future<Widget> build(BuildContext context) async {

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);
},
);
}
}