How to get the global context with GetX in flutter - flutter-getx

How to get the global context with GetX in flutter?
I encapsulated a toast but how can I get the context?
Is there any other way to get the context?
CommonWidget.dart
class CommonWidget {
static toast(String error) async {
BrnToast.show(
error,
context,
gravity: BrnToastGravity.bottom,
background: Color.fromRGBO(0, 0, 0, 0.5),
radius: 3,
duration: BrnDuration.long,
);
}
}
LoginController.dart
class LoginController extends GetxController {
verificationCode() async {
CommonWidget.toast(res.message.toString());
}
}

Related

Flutter Bloc-Cubit Retain details from other states

I have below CategoryState in my project:
part of 'categories_cubit.dart';
abstract class CategoriesState {}
class CategoriesInitial extends CategoriesState {}
class CategoriesLoaded extends CategoriesState {
final List<Category> categories;
final List<Category>? filteredData;
final int? sortIndex;
final bool sortAscending;
CategoriesLoaded({
required this.categories,
this.filteredData,
this.sortIndex,
required this.sortAscending,
});
}
//Add
class AddingCategory extends CategoriesState {}
class CategoryAdded extends CategoriesState {}
//delete
class DeletingCategory extends CategoriesState {}
class CategoryDeleted extends CategoriesState {}
//update
class UpdatingCategory extends CategoriesState {}
class CategoryUpdated extends CategoriesState {}
//error
class CategoryStateError extends CategoriesState {
String? errorMessage = "Error encoutered";
CategoryStateError({this.errorMessage});
}
Then I have this Category Page with DataTable loaded by the categories from if state is CategoriesLoaded.
class _CategoryPageState extends State<CategoryPage> {
final searchController = TextEditingController();
#override
Widget build(BuildContext context) {
return SizedBox(
width: 1500,
height: 1000,
child: BlocBuilder<CategoriesCubit, CategoriesState>(
builder: (context, state) {
if (state is! CategoriesLoaded) {
if (state is CategoriesInitial) {
BlocProvider.of<CategoriesCubit>(context).fetchCategories();
}
return const Center(child: CircularProgressIndicator());
}
return Container(....)
.
.
.
}
From the Category Page, I can open an Add Item Dialog. If adding category is successful, Cubit will emit CategoryAdded state and close the dialog. Otherwise, cubit will emit CategoryStateError state.
My problem is that once CategoryStateError state is emit, the main Category Page beneath the dialog box becomes empty since its display data depends on CategoryLoaded state. Is there anyway where I can retain the data in Category Page even if the state is changed in the Dialog Box operation? Or is there any better alternative for error handling using Bloc Cubit
Try this :
class _CategoryPageState extends State<CategoryPage> {
final searchController = TextEditingController();
#override
Widget build(BuildContext context) {
return SizedBox(
width: 1500,
height: 1000,
child: BlocBuilder<CategoriesCubit, CategoriesState>(
listener: (context, state) {
if (state is! CategoriesLoaded) {
if (state is CategoriesInitial) {
BlocProvider.of<CategoriesCubit>(context).fetchCategories();
}
}
},
builder: (context, state) {
return (state is! CategoriesLoaded && state is! CategoriesInitial)?const Center(child: CircularProgressIndicator()):Container(....)
.
.
.
}
I managed to solve my issue. Instead of creating CategoryStateError to handle the Exception, I created a new Controller and Stream where I add the error message. I do not emit a new state when there is an exception. I monitor the stream and add display the error message.

Flutter: Countdown Timer

I am trying to create a timer app that has multiple countdown timers for different tasks. The issue, I am facing is that, if I start a one-timer, and press the back button, the timer stops. So I want, that timer to run till either it is being paused or the timer ends and alerts the user or the app is destroyed. Help me how can I do this using Flutter?
Any Sample Code Will be Appreciated?
enter link description here
CountDownController _controller = CountDownController();
CircularCountDownTimer(
width: MediaQuery.of(context).size.width / 6,
height: MediaQuery.of(context).size.width / 6,
duration: 120,
fillColor: Colors.green,
ringColor: Colors.white,
controller: _controller,
backgroundColor: Colors.white54,
strokeWidth: 10.0,
strokeCap: StrokeCap.round,
isTimerTextShown: true,
isReverse: false,
onComplete: () {
Notify();
},
textStyle: TextStyle(fontSize: 20.0, color:
Colors.black),
),
When you pop back, any "state" in the widget will be destroyed.
There are three kinds of method you can do to prevent "state" being destroyed (or memory release):
Using static property
Using state manager by Provider
Using state manager by static instance
There are still many method to manage your state, but not mention here, see details in this repo
Static property
Static property is something like variable outside your class, like:
// prefix "_" for private variable
const _myStaticVar = 'hello world';
class MyWidget {}
Rather, it is class based variable. Which means it can help you describe the variable more. like class Dog can has a static property static final footShouldHave = 4. Class based programing is popular because it can manage your state and any logic action "inside" the class, and make it easier to understand and code.
When the class is being destroyed (memory release), any "state" inside the class should be pop from stack but not static. You can see more detail by knowing how compiler works.
In your case, you can do something like:
class MyTimer extends StatlessWidget {
static DateTime? starter;
Widget build(context) {
if (starter == null) {
starter = DateTime.now();
}
final secondPass = (DateTime.now().millisecondsSinceEpoch - starter!.millisecondsSinceEpoch) / 1000;
final secondLeft = 60 - secondPass;
return Text(secondLeft.toString());
}
}
Provide state manager by Provider
Provider is made for flutter and also maintained by flutter team. It can make you easy to manage your class by accessing it from context.
You can also set up how the class create.
lazy, create only when you need it
create in future
...
In your case, it should be like:
Build your helper class TimerManager
class TimerManager {
final DateTime? starter;
void startIfNeed() {
if (starter != null) {
starter = DateTime.now();
}
}
num get secondLeft => 60 - (DateTime.now().millisecondsSinceEpoch - starter!.millisecondsSinceEpoch) / 1000
}
Bind with Provider
class Homepage extends statelessWidget {
Widget build(context) {
return TextButton(
onPressed: () => navigateToTimer(context),
child: Text('go'),
);
}
void navigateToTimer(Build context) {
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => MyTimer()),
);
}
}
void main() {
runApp(MaterialApp(
home: Provider<TimerManager>(
create: () => TimerManager(),
child: Homepage(),
)
));
}
Get it from your context.
Now when your widget is released, it is still existed in parent context (if their do exist a parent).
// remember to import provider to able `context.read()`.
// see more detail in document.
import 'package:provider/provider.dart';
class MyTimer extends StatlessWidget {
Widget build(context) {
final manager = context.read<TimerManager>();
manager.startIfNeed();
return Text(manager.secondLeft.toString());
}
}
static instance
Kind of combined method from 1 and 2.
class TimerManager {
// make it singleton
static final TimerManager instance = TimerManager._();
// It is now private constructor
const TimerManager._();
...
}
Just call it in your widget
class MyTimer extends StatlessWidget {
Widget build(context) {
TimerManager.instance.startIfNeed();
return Text(TimerManager.instance.secondLeft.toString());
}
}
Summary
There is no best way to keep your state in generally, but in your case, I recommend Provider method.

I get an IllegalStateException sometimes in flutter app after adding native ads to a listview

this is the error I get:
setState() called after dispose(): _PlatformViewLinkState#bc2a5(lifecycle state: defunct, not mounted)
This error happens if you...
E/flutter ( 4758): [ERROR:flutter/shell/platform/android/platform_view_android_jni_impl.cc(49)] java.lang.IllegalStateException: PlatformView#getView() returned null, but an Android view reference was expected.
I'm using the google_mobile_ads package and not the firebase_ads one since it's deprecated.
I'm trying to put native ads in a list of cards, after a fixed number of elements in the listview.
The way I did this is by using a provider in the main.dart file:
...
void main() {
WidgetsFlutterBinding.ensureInitialized();
final initFuture = MobileAds.instance.initialize();
final adState = AdState(initFuture);
runApp(Provider.value(
value: adState,
builder: (context, child) => MyApp(),
));
}
...
the screen containing the listview is called chapters_screen.dart, when going to this screen the app sometimes crashes. In this file there is a list of items List<Object> itemList = []; as a state variable. In init state the list is populated with offline data as so: itemList = List.from(chaptersData);,
this is how I add the ads in the list:
#override
void didChangeDependencies() {
super.didChangeDependencies();
final adState = Provider.of<AdState>(context);
if(this.mounted){
adState.initialization.then((status) {
if(this.mounted){
setState(() {
for (int i = itemList.length - 2; i >= 1; i -= 2) {
itemList.insert(
i,
// BannerAd(
// adUnitId: adState.bannerAdUnitId,
// size: AdSize.banner,
// request: AdRequest(),
// listener: adState.adListener,
// )..load(),
NativeAd(
adUnitId: adState.nativeAdUnitId,
factoryId: 'listTile',
request: AdRequest(),
listener: NativeAdListener(
onAdLoaded: (_) {
print('ad is loaded succesfully!');
},
onAdFailedToLoad: (ad, error) {
// Releases an ad resource when it fails to load
ad.dispose();
print('Ad load failed (code=${error.code} message=${error.message})'); },
),
)..load(),
);
}
});
}
});
}
}
I tried it before with a banner ad and there is no problem. I followed this tutorial: https://www.youtube.com/watch?v=m0d_pbgeeG8 but I had to change the google_mobile_ads package version to a newer one to be able to use the same NativeAdFactory as in the flutter documentation. The problem is in the newer version the AdListener object no longer exists so instead of passing: listener: adState.adListener like in the video. I put the listener as the code above in the 'NativeAd()' widget.
this is how I'm checking for the type of the listItem to build it in the listView:
Widget _buildListView(BuildContext context) {
List<Widget> widgetList = [];
for (int i = 0; i < itemList.length; i++) {
if (itemList[i] is NativeAd) {
widgetList.add(Container(
height: 200,
child: AdWidget(ad: itemList[i] as NativeAd),
color: Colors.black));
} else {
widgetList.add(ChapterCard(chapter: Chapter.fromJson(itemList[i])));
}
}
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/blured_background.jpg"),
fit: BoxFit.cover,
),
),
child: ListView(children: widgetList.toList()),
);
}
I think I'm doing something wrong in the provider or in the factory class but I'm not sure.
the factory class for android (in java):
import com.google.android.gms.ads.nativead.NativeAd;
import com.google.android.gms.ads.nativead.NativeAdView;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.Map;
import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin;
class ListTileNativeAdFactory implements GoogleMobileAdsPlugin.NativeAdFactory {
private final Context context;
ListTileNativeAdFactory(Context context) {
this.context = context;
}
#Override
public NativeAdView createNativeAd(
NativeAd nativeAd, Map<String, Object> customOptions) {
NativeAdView nativeAdView = (NativeAdView) LayoutInflater.from(context)
.inflate(R.layout.list_tile_native_ad, null);
TextView attributionViewSmall = nativeAdView
.findViewById(R.id.tv_list_tile_native_ad_attribution_small);
TextView attributionViewLarge = nativeAdView
.findViewById(R.id.tv_list_tile_native_ad_attribution_large);
ImageView iconView = nativeAdView.findViewById(R.id.iv_list_tile_native_ad_icon);
NativeAd.Image icon = nativeAd.getIcon();
if (icon != null) {
attributionViewSmall.setVisibility(View.VISIBLE);
attributionViewLarge.setVisibility(View.INVISIBLE);
iconView.setImageDrawable(icon.getDrawable());
} else {
attributionViewSmall.setVisibility(View.INVISIBLE);
attributionViewLarge.setVisibility(View.VISIBLE);
}
nativeAdView.setIconView(iconView);
TextView headlineView = nativeAdView.findViewById(R.id.tv_list_tile_native_ad_headline);
headlineView.setText(nativeAd.getHeadline());
nativeAdView.setHeadlineView(headlineView);
TextView bodyView = nativeAdView.findViewById(R.id.tv_list_tile_native_ad_body);
bodyView.setText(nativeAd.getBody());
bodyView.setVisibility(nativeAd.getBody() != null ? View.VISIBLE : View.INVISIBLE);
nativeAdView.setBodyView(bodyView);
nativeAdView.setNativeAd(nativeAd);
return nativeAdView;
}
}
The MainActivity class:
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin;
public class MainActivity extends FlutterActivity {
#Override
public void configureFlutterEngine(#NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
// TODO: Register the ListTileNativeAdFactory
GoogleMobileAdsPlugin.registerNativeAdFactory(flutterEngine, "listTile",
new ListTileNativeAdFactory(getContext()));
}
#Override
public void cleanUpFlutterEngine(#NonNull FlutterEngine flutterEngine) {
super.cleanUpFlutterEngine(flutterEngine);
// TODO: Unregister the ListTileNativeAdFactory
GoogleMobileAdsPlugin.unregisterNativeAdFactory(flutterEngine, "listTile");
}
}

How can I get an extension method to change the original object in Dart?

I have bounds. bounds should be extended with new LatLng.
var bounds = LatLngBounds();
for (var latlng in _list) bounds.extend(latlng);
I want to implement this extension:
extension LatLngBoundsExtend on LatLngBounds {
extend(LatLng _latLng){
//I want to change the object which this method is called from
this = LatLngBounds(southwest: /* some magic code*/, northeast: /* some magic code*/ );
}
}
It would have been possible to change the original object if its properties were not final. This is not the case for LatLngBounds.southwest and LatLngBounds.northeast.
If I remember correctly, LatLngBounds is an immutable class representing a latitude/longitude aligned rectangle.
final myLatLngBounds = LatLngBounds(southwest, northeast);
myLatLngBounds.extend(latlng);
If this extend modifies this how would you access this? 😮
I think this extend should rather be part of some kind of State Management.
See the following, using Riverpod Hooks package:
Instead of changing the object (since it is immutable), you return a new one in your extension:
extension RectX on Rect {
Rect extend(Offset offset) {
return Rect.fromLTRB(
min(left, offset.dx),
min(top, offset.dy),
max(right, offset.dx),
max(bottom, offset.dy),
);
}
}
Then, when you need to modify the object, you work on a State Object managed by Flutter or any other State Management System:
final rectProvider =
StateNotifierProvider<RectNotifier>((ref) => RectNotifier());
class RectNotifier extends StateNotifier<Rect> {
RectNotifier([Rect state])
: super(state ?? Rect.fromLTRB(100, 100, 200, 200));
void extend(Offset offset) {
state = state.extend(offset);
}
}
Here is a Minimal Working Example:
import 'dart:math' show min, max;
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
void main() {
runApp(
ProviderScope(
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
),
);
}
class HomePage extends HookWidget {
#override
Widget build(BuildContext context) {
final rect = useProvider(rectProvider.state);
return Scaffold(
body: GestureDetector(
onTapDown: (details) =>
context.read(rectProvider).extend(details.localPosition),
child: Container(
color: Colors.black12,
padding: EdgeInsets.all(16.0),
child: Stack(
children: [
Positioned(
top: rect.top,
left: rect.left,
child: Container(
width: rect.width,
height: rect.height,
color: Colors.amber.shade400),
),
],
),
),
),
);
}
}
final rectProvider =
StateNotifierProvider<RectNotifier>((ref) => RectNotifier());
class RectNotifier extends StateNotifier<Rect> {
RectNotifier([Rect state])
: super(state ?? Rect.fromLTRB(100, 100, 200, 200));
void extend(Offset offset) {
state = state.extend(offset);
}
}
extension RectX on Rect {
Rect extend(Offset offset) {
return Rect.fromLTRB(
min(left, offset.dx),
min(top, offset.dy),
max(right, offset.dx),
max(bottom, offset.dy),
);
}
}
You can change the fields if they are not final. But you cannot change object as this = new Class().
PS.
southwest and northeast are final fields of LatLngBounds. So you can't change them with an extension
https://github.com/flutter/plugins/blob/master/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart
Extension methods can mutate this, but they cannot reassign/rebind it. Note that that is true for normal methods too and isn't specific to extension methods.
Suppose that reassigning this were possible. Consider the following code:
var foo = Foo();
var sameFoo = foo;
foo.reassignThis();
Would foo and sameFoo still refer to the same object? It would be surprising if they now referred to different objects. However, if they do still refer to the same object, that would mean that the VM/runtime would need to be able to easily and quickly find all references to an object so that it can update them.
Even if the VM/runtime could do that, then consider:
class Base {
Base(this.x);
int x;
void reassignThis() {
this = Base(x + 1); // Not legal!
}
}
class Derived extends Base {
Derived(int x) : super(x);
int y;
}
void main() {
var derived = Derived(0);
derived.reassignThis();
print(derived.y);
}
After calling reassignThis(), what would derived be? Would it still be a Derived object? Would it be just a Base object?
Reassigning this isn't possible, and it isn't something that could be improved; fundamentally it doesn't make much sense.
Now, instead of reassigning this, you could implement something like:
class MyLatLngBounds implements LatLngBounds {
MyLatLngBounds(this._actual);
LatLngBounds _actual;
LatLng get northeast => _actual.northeast;
LatLng get southwest => _actual.southwest;
// ... other methods that forward to [_actual] ...
void extend(LatLng _latLng) {
_actual = LatLngBounds(/* some magic code */);
}
}
and then try to use MyLatLngBounds everywhere instead of LatLngBounds.

Bloc - Is it possible to yield states for a previous page in the navigation stack?

I have a BlocBuilder which handles building widgets depending on the yielded state for my dashboard page.
body: BlocBuilder<DashboardBloc, DashboardState>(
builder: (context, state) {
print(state);
if (state is DashboardInitial) {
return loadingList();
} else if (state is DashboardEmpty) {
return emptyList();
} else if (state is DashboardLoaded) {
return loadedList(context, state);
}
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => AddPage()));
},
I want to be able to navigate to the add page, fill in some textfields, and then dispatch an event to my dashboard bloc, with the idea being that upon navigating back to the dashboard, my list will be updated.
class AddPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
TextEditingController titleController = TextEditingController();
TextEditingController descriptionController = TextEditingController();
return Scaffold(
appBar: AppBar(title: Text('Add')),
body: Container(
padding: EdgeInsets.all(10),
child: Column(
children: [
TextField(
controller: titleController,
),
TextField(
controller: descriptionController,
),
RaisedButton(onPressed: () {
BlocProvider.of<DashboardBloc>(context)
.add(DashboardWorryAdded('title', 'description'));
}),
],
),
),
);
}
}
When following the code using breakpoints, i am able to see that my state is yielded in the 'mapeventtostate' function, however my dashboard is never rebuilt with the new values.
Here is the code for my Bloc, events, and states. My first thought would be that Equatable was detecting the same state being returned, but upon removing Equatable, my problem is still persists.
#override
Stream<DashboardState> mapEventToState(
DashboardEvent event,
) async* {
if (event is DashboardWorryAdded) {
yield* _mapDashboardWorryAddedToState(event);
} else if (event is DashboardLoading) {
yield* _mapDashboardLoadingToState(event);
} else if (event is AppStarted) {
yield* _mapAppStartedToState(event);
}
}
Stream<DashboardState> _mapAppStartedToState(AppStarted event) async* {
List<Worry> _wList = await repo.getAllWorries();
if (_wList.length != 0) {
yield DashboardLoaded(worryList: _wList);
} else {
yield DashboardEmpty();
}
}
Stream<DashboardState> _mapDashboardLoadingToState(
DashboardLoading event) async* {
List<Worry> _wList = await repo.getAllWorries();
if (_wList != 0) {
yield DashboardLoaded(worryList: _wList);
} else {
yield DashboardEmpty();
}
}
Stream<DashboardState> _mapDashboardWorryAddedToState(
DashboardWorryAdded event) async* {
await repo.addWorry(event.title, event.description);
List<Worry> worryList = List<Worry>();
worryList = await repo.getAllWorries();
yield DashboardLoaded(worryList: worryList);
}
}
#immutable
abstract class DashboardEvent {}
class DashboardLoading extends DashboardEvent {
DashboardLoading();
}
class DashboardWorryAdded extends DashboardEvent {
final String title, description;
DashboardWorryAdded(this.title, this.description);
}
class AppStarted extends DashboardEvent {
AppStarted();
}
#immutable
abstract class DashboardState {}
class DashboardInitial extends DashboardState {
DashboardInitial();
}
class DashboardLoaded extends DashboardState {
final List<Worry> worryList;
DashboardLoaded({this.worryList});
}
class DashboardEmpty extends DashboardState {
DashboardEmpty();
}
Instead of trying to mutate another page's state (a bit of a no-no where state management is concerned), take advantage of the fact that the push method of the navigator returns a future that completes when that page gets popped, and as a bonus, the value of the future will include the value that was given to the pop method in the other page. So you can now do something like this:
class DashboardBloc {
...
void showAddPage() async {
// Do this to retrieve the value passed to the add page's call to `pop`
final value = await Navigator.of(context).push(...);
// Do this if the add page doesn't return a value in `pop`
await Navigator.of(context).push(...);
// Either way, you can now refresh your state in response to
// the add page popping
emit(...);
}
}
Note: This works just as well for named routes too.