I'm using FCM to send push notifications to my device right now and it's working perfectly. However when the app is open, I only get the onResume to be executed when I'm in that particular page. I want to display the notification on the top regardless of which page(or class) the user is on. Basically I want the notifications to be displayed globally (Show popup). Any help would be appreciated. Here is the code from the page that displays the notifications.
if (Platform.isIOS) {
iosSubscription = _fcm.onIosSettingsRegistered.listen((data) {
_saveDeviceToken();
});
_fcm.requestNotificationPermissions(IosNotificationSettings());
} else {
_saveDeviceToken();
}
_fcm.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
var temp = message['notification'];
setState(() {
title.add(temp['title']);
body.add(temp['body']);
});
showDialog(
context: context,
builder: (context) => AlertDialog(
content: ListTile(
title: Text(message['notification']['title']),
subtitle: Text(message['notification']['body']),
),
actions: <Widget>[
FlatButton(
color: const Color(0xFF650572),
child: Text('Ok'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
},
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
Navigator.push(
context, MaterialPageRoute(builder: (context) => MessageHandler()));
// TODO optional
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
Navigator.push(context,
MaterialPageRoute(builder: (context) => MessageHandler()));
// TODO optional
},
);
}
Wrap your MaterialApp in a wrapper class ... lets call that FCMWrapper.
class FCMWrapper extends StatefulWidget {
final Widget child;
const FCMWrapper({Key key, this.child}) : super(key: key);
#override
_FCMWrapperState createState() => _FCMWrapperState();
}
class _FCMWrapperState extends State<FCMWrapper> {
#override
Widget build(BuildContext context) {
return Consumer<YourObservable>(
builder: (context, yourObservable, _) {
if (yourObservable != null && yourObservable.isNotEmpty) {
Future(
() => navigatorKey.currentState.push(
PushNotificationRoute(
child: YourViewOnNotification(),
)),
),
);
}
return widget.child;
},
);
}
}
I have stored my data in an observable from a separate class. So when I receive a notification I update my observable. Since we are consuming on that observable the PushNotificationRoute would be called.
PushNotificationRoute is simply a class which extends ModalRoute.
class PushNotificationRoute extends ModalRoute {
final Widget child;
PushNotificationRoute({this.child});
... //Override other methods to your requirement
//This is important
#override
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return SafeArea(
child: Builder(builder: (BuildContext context) {
return child;
}),
);
}
...
#override
Duration get transitionDuration => Duration(milliseconds: 200);
}
Now in main.dart declare a global key like
var navigatorKey = GlobalKey<NavigatorState>();
and wrap your MaterialApp like
...
FCMWrapper(
child: MaterialApp(
navigatorKey: navigatorKey,
title: 'Your App',
...
So now every time a notification comes in your observable should update and push a modal route which would be shown over anywhere in the app.
Related
Please help.
I'm trying to display the photo captured in 'Generated1Group1Widget1.dart' to 'GeneratedResultsWidget.dart'. However, 'main.dart' is having some errors.
GeneratedGroup1Widget1.dart
class GeneratedGroup1Widget1 extends StatefulWidget {
#override
_GeneratedGroup1Widget1State createState() => _GeneratedGroup1Widget1State();
}
class _GeneratedGroup1Widget1State extends State<GeneratedGroup1Widget1> {
XFile? _image;
Future _pickImage() async {
final imageSource = await showDialog<ImageSource>(
context: context,
builder: (context) => SimpleDialog(
title: const Text('Select Image Source'),
children: [
SimpleDialogOption(
onPressed: () => Navigator.pop(context, ImageSource.camera),
child: const Text('Camera'),
),
SimpleDialogOption(
onPressed: () => Navigator.pop(context, ImageSource.gallery),
child: const Text('Gallery'),
),
],
),
);
if (imageSource != null) {
final image = await ImagePicker().pickImage(source: imageSource);
setState(() {
_image = image;
});
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => GeneratedResultsWidget(image: _image),
),
);
}
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => _pickImage(),
...
GeneratedResultsWidget.dart
class GeneratedResultsWidget extends StatelessWidget {
final XFile? image;
GeneratedResultsWidget({
required this.image,
});
#override
Widget build(BuildContext context) {
return Material(
child: ClipRRect(
...
main.dart
void main() {
runApp(food_classifierApp());
}
class food_classifierApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: Size(360, 640),
builder: (BuildContext context,child) => MaterialApp(
title: 'food-classifier',
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: '/GeneratedHomepageWidget',
routes: {
'/GeneratedScanWidget': (context) => GeneratedScanWidget(),
'/GeneratedResultsWidget': (context) => GeneratedResultsWidget(image: _image),
'/GeneratedHomepageWidget': (context) => GeneratedHomepageWidget(),
'/GeneratedFoodlistWidget': (context) => GeneratedFoodlistWidget(),
},
),
);
}
}
Error
Undefined name '_image'.
Try correcting the name to one that is defined, or defining the name.
I already searched up google but I can't find answers to my question. Thanks in advance!
Where exactly you define _image variable in your food_classifierApp class?
You should use state management to access your image or ... in every where of your app
You need to create model to store your picked image file from GeneratedGroup1Widget1.
class AppModel {
String fileName;
AppModel(this.fileName);
}
Now you can call it from GeneratedGroup1Widget1
Navigator.pushNamed(context, '/GeneratedResultsWidget',
arguments: AppModel('your file or file name'));
And recieve it here
class GeneratedResultsWidget extends StatelessWidget {
const GeneratedResultsWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final args = ModalRoute.of(context)!.settings.arguments as AppModel;
return Scaffold(body: Center(child: Text(args.fileName)));
}
}
Route should be
routes: {
//***
'/GeneratedResultsWidget': (context) => GeneratedResultsWidget(),
//***
}
I'm having problems with my code as I'm getting undefined_identifier errors.
I'm trying to pass the image I captured from 'GeneratedGroup1Widget1.dart' using 'flutter_screenutils' to 'GeneratedResultsWidget.dart'. However, my route in 'main.dart' doesn't define the image variable inside my parameter. I've been trying to fix this error for 10 hours now. Please help. Thanks in advance!
Error: (main.dart)
Undefined name 'image'.
Try correcting the name to one that is defined, or defining the name.
Here are my codes:
'GeneratedGroup1Widget1.dart'
class GeneratedGroup1Widget1 extends StatefulWidget {
#override
_GeneratedGroup1Widget1State createState() => _GeneratedGroup1Widget1State();
}
class _GeneratedGroup1Widget1State extends State<GeneratedGroup1Widget1> {
Future _pickImage() async {
final imageSource = await showDialog<ImageSource>(
context: context,
builder: (context) => SimpleDialog(
title: const Text('Select Image Source'),
children: [
SimpleDialogOption(
onPressed: () => Navigator.pop(context, ImageSource.camera),
child: const Text('Camera'),
),
SimpleDialogOption(
onPressed: () => Navigator.pop(context, ImageSource.gallery),
child: const Text('Gallery'),
),
],
),
);
if (imageSource != null) {
final image = await ImagePicker().pickImage(source: imageSource);
Navigator.pushNamed(context, '/GeneratedResultsWidget', arguments: image);
}
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => _pickImage(),
child: Container(
....
'GeneratedResultsWidget.dart'
class GeneratedResultsWidget extends StatelessWidget {
final XFile file;
const GeneratedResultsWidget({required Key key, required this.file})
: super(key: key);
#override
Widget build(BuildContext context) {
if (file == null) {
return Scaffold(body: Center(child: Text('No Image selected')));
} else {
return Scaffold(body: Center(child: Text(file.path)));
}
}
}
class GeneratedResultsWidget1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Material(
...
'main.dart'
void main() {
runApp(FoodClassifierApp());
}
class FoodClassifierApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: Size(360, 640),
builder: (BuildContext context, child) => MaterialApp(
title: 'food-classifier',
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: '/GeneratedHomepageWidget',
routes: {
'/GeneratedScanWidget': (context) => GeneratedScanWidget(),
'/GeneratedResultsWidget': (context) =>
GeneratedResultsWidget(key: UniqueKey(), file: image),
'/GeneratedHomepageWidget': (context) => GeneratedHomepageWidget(),
'/GeneratedFoodlistWidget': (context) => GeneratedFoodlistWidget(),
},
),
);
}
}
This is my first time coding in flutter and I used figma to generate my widgets.
My problem is this. I created cubit for 2 different pages. When I am on the first page, I can fill the list inside the 2nd page and I can read it from the log. However, when I go to the second page, the list I filled in from the previous page is still empty.
Main.dart
home:
MultiBlocProvider(
providers: [
BlocProvider(create: (_) => HomeCubit(PhotoService())),
BlocProvider(create: (_)=> FavoritesCubit())//Gerekiyor,homeviewda içerisindeki methoda erişmem gerekiyor
],
child: const HomeView())
HomeView.dart
where I run the function in favoritescubit and add it to the list
onTap: () {
BlocProvider.of<FavoritesCubit>(context).addFavorite(
context,
state.selectItem![index],
);
print(state.selectItem?[index].isSelected);
context.read<FavoritesCubit>().getAllFavorites();
// print("UI --- ${state.selectItem![index].isSelected}");
// context.read<FavoriteBloc>().add(
// AddFavorite(photoList, photoList.isSelected));
// print(" ispressed ${photoList.isSelected}");
},
FavoritesCubit.dart
class FavoritesCubit extends Cubit<FavoritesState> {
FavoritesCubit() : super(const FavoritesState());
final List<PhotoModel> favoriteList = <PhotoModel>[];
Future<void> getAllFavorites() async {
print("FavoriteList : ${favoriteList.length}");
emit(state.copyWith(favoriteList: favoriteList));
}
Future<void> addFavorite(
BuildContext context,
PhotoModel photo,
) async {
photo.isSelected = !photo.isSelected;
if (favoriteList.contains(photo) == false) {
favoriteList.add(photo);
emit(state.copyWith(
favoriteList: favoriteList, isFavorite: photo.isSelected));
print("${state.favoriteList!.length}asdasd");
} else if (favoriteList.contains(photo) == true) {
favoriteList.remove(photo);
emit(state.copyWith(
favoriteList: favoriteList, isFavorite: photo.isSelected));
print("${state.favoriteList!.length}asdasd");
}
FavoriteView.dart
class FavoriteView extends StatefulWidget {
const FavoriteView({Key? key}) : super(key: key);
#override
State<FavoriteView> createState() => _FavoriteViewState();
}
class _FavoriteViewState extends State<FavoriteView> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => FavoritesCubit()..getAllFavorites(),
child: Scaffold(
appBar: AppBar(
title: const Text("Bloc Example"),
),
body: buildFavoriteList(context),
),
);
}
}
Widget buildFavoriteList(BuildContext context) {
return BlocConsumer<FavoritesCubit, FavoritesState>(
listener: (context, state) {
// TODO: implement listener
},
builder: (context, state) {
return ListView.builder(
itemCount: state.favoriteList?.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: (() {
// navigateToPostDetailPage(context, photos[index]);
}),
child: Padding(
padding: const EdgeInsets.all(10),
child: PhotoListTile(
isPressed: state.favoriteList![index].isSelected,
imageUrl: state.favoriteList![index].thumbnailUrl.toString(),
title: state.favoriteList![index].title.toString(),
url: state.favoriteList![index].url.toString(),
onTap: () {
// context.read<HomeCubit>().addFavorite(
// context,
// state.favoriteList![index],
// state.favoriteList![index].isSelected);
// context
// .read<FavoriteBloc>()
// .add(RemoveFavorite(photos[index]));
},
),
),
);
});
},
);
Okey, now with your added information about your FavoriteView it is clear what the problem is.
In your FavoriteView you create a new cubit, which is not the same as you created in the MultiBlocProvider. That is why it is always empty on your FavoriteView
create: (context) => FavoritesCubit()..getAllFavorites(), // This is the issue
Make sure that your FavoriteView is a child somewhere under your MultiBlocProvider and remove the creation of a new FavoritesCubit in that view. I.e. remove the BlocProvider in your FavoriteView
I am trying to display an Alert that shows a disclaimer to the user as soon as the app is opened. The build method will run, that is the app will start its processing only after the user presses okay on the alert.
I've managed to show the alert in init using
SchedulerBinding.instance.addPostFrameCallback((_) => AlertWindow().showAlert(context));
or
Future.delayed(Duration.zero, () => AlertWindows().showAlert(context));
This shows the alert, but the app starts building in the background. I want the app to run/build only after OKAY button is pressed, and after the alert is popped.
Hey I implemented some code, you can try this code directly on dartPad Paste the code in this Editor
I used setState, if it is for real time project you can use Providers or bloc, for performance.
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
Widget viewHolder;
void initState() {
viewHolder = Container();
WidgetsBinding.instance
.addPostFrameCallback((_) => afterPostFrameCallBack());
super.initState();
}
afterPostFrameCallBack() {
_showDialog();
}
#override
Widget build(BuildContext context) {
return viewHolder;
}
Widget _buildView() {
return Container(child: Text('This is after okay button'));
}
void _showDialog() {
// flutter defined function
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: Text("App Update Available"),
content: Text(
"We have fixed some issues and added some cool features in this update"),
actions: <Widget>[
// usually buttons at the bottom of the dialog
FlatButton(
child: new Text("ok"),
onPressed: () {
Navigator.of(context).pop();
setState(() {
viewHolder = _buildView();
});
},
),
],
);
},
);
}
}
I'm having a problem with my app.
The situation is: i have a very simple login system on the app and i save the logged user using SharedPreferences.
But if the user leaves the app and then return it will open the login screen again, so i want to skip the login screen if the user is logged.
So in my main i put a function to check if there is login information, if yes it would redirect right to the app page or to the login page if not.
But when i try to call the app page it always calls the page setted on the Home part.
How can i solve this?
Is there any way to make it ignore the Home?
Is there a way to make the "if" part on the home? Would be the better solution but its not possible.
Also i know i'm not using the best way to make this control, but it works (despite of this problem i have now, sure) and if you have any tips on making it better i would appreciate.
Heres my code:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
void main() => runApp(new MyApp());
class _MyAppState extends State<MyApp> {
Future<void> verificaLogin() async {
print("running ok"); //just to test if the function runs
final prefs = await SharedPreferences.getInstance();
final key = 'usuario';
final value = prefs.getString(key);
print('saved tester $value');
String usu = value; /
if (usu.isEmpty) {
BuildContext context;
Navigator.push(
context,
MaterialPageRoute(builder: (context) => LoginScreen()), //sends to loginscreen if not logged
);
}
if (usu.isNotEmpty) {
BuildContext context;
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (context) => Pedidos())); //sends to main (not main.dart) app page
}
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => verificaLogin());
}
Widget build(BuildContext context) {
return BotToastInit(
child: MaterialApp(
navigatorObservers: [BotToastNavigatorObserver()],
title: "Test App",
theme: ThemeData(
primarySwatch: Colors.green,
),
debugShowCheckedModeBanner: false,
home: LoginScreen(), //i'm calling the loginscreen, it ignores the function on the top
),
);
}
}
Please make one SplashScreen like below it resolves your issue..
and call this as home: SplashScreen(),
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
String loginData = "";
SharedPreferences sharedPreferences;
void initState() {
super.initState();
readFromStorage();
}
void readFromStorage() async {
sharedPreferences = await SharedPreferences.getInstance();
final key = 'usuario';
loginData = sharedPreferences.getString(key);
if (loginData.isNotEmpty) {
Future.delayed(const Duration(milliseconds: 3000), () {
setState(() {
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (context) => Pedidos()));
});
});
} else {
Future.delayed(const Duration(milliseconds: 3000), () {
setState(() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => LoginScreen()),);
});
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlutterLogo(
size: 100.0,
),
],
)),
);
}
}
Hi you can use a FutureBuilder at vefiricaLoigin, and then Material App at home use verificaLogin,
https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
verificaLogin() {
return FutureBuilder<Widget>(
future: SharedPreferences.getInstance(),
builder: (BuildContext context, prefs) {
final key = 'usuario';
final value = prefs.getString(key);
String usu = value;
if (usu.isEmpty) {
return LoginScreen()l
} else {
return Pedidos();
}
);
}
Widget build(BuildContext context) {
return MaterialApp(
title: "Test App",
theme: ThemeData(
primarySwatch: Colors.green,
),
debugShowCheckedModeBanner: false,
home: verificaLogin()
);
}