How to check whether screen reader is on using Flutter - flutter

We are in are requirement of handling different level of user experience for the user who are using screen readers.
We need to implement some logic only if the screen reader is enabled.
if(isScreenReaderOn){
logic A goes here
} else {
logic B goes here
}
But I was not able to find out a way in Flutter to check whether the screen reader of the device is on.
I went through the following links but I was not able to find out a clear solution.
https://github.com/flutter/flutter/issues/12436
And here.
They seem use it internally but I can't realize it clearly to apply in my case.
Really appreciate if you can support on this.

Here a sample code
void main(){
runApp(MaterialApp(
home: MyWidget(),
));
}
class MyWidget extends StatelessWidget {
Widget build(BuildContext context) {
// retrieve the mediaQuery data
final mediaQueryData = MediaQuery.of(context);
if (mediaQueryData.accessibleNavigation) {
return Text('Screen reader is on');
} else {
return Text('Screen reader is off');
}
}
}
Update:
I tested the code with enabling magnification and select-to-speak features.. but no change happened of the output. As It originated from accessibleNavigation property of [Window.AccessibilityFeatures],. It results whether there is a running accessibility service which is changing the interaction model of the device. I think it is only looking for Screen Readers.

Related

Flutter Google Maps blank on app resume with multiple pages displaying map

I have an issue with showing GoogleMaps in my Flutter app, I've implemented it in a swiper, so each page should show the location with a custom marker, would post a code sample but it's too long so I'll try to explain it as short as I can.
Basically when I open the swiper with detail pages in it, it has a map in the bottom and it shows fine while swiping through the pages, but if I put the app in background and get back to it after few swipes it shows just white container with Google logo but no map.
It happens on physical devices but not on the Emulator.
Found the issue opened here
Also tried suggestions from this question
If anyone has an idea on how to fix it, it would be appreciated
I had this issue but didn't realize it was a reported bug. Trying to track down the problem I tried a brute-force mark-the-whole-UI-dirty on foregrounding to see if might be a redraw issue and it stopped happening. Not elegant but it seems to work. None of our testers have reported the blank Google Maps since. I also found I had to do it once Flutter was ready to build() after foregrounding.
This is in the stateful widget that is the first child of my MultiProvider at the top of the app. The google map is in a Scaffold()->Stack() farther down the graph:
#override
void didChangeAppLifecycleState(AppLifecycleState state) async {
if(state == AppLifecycleState.resumed) {
setState(() { _refreshAllChildrenAfterWakeup = true; });
}
}
void _rebuildAllChildren(BuildContext context) {
void rebuild(Element el) {
el.markNeedsBuild();
el.visitChildren(rebuild);
}
(context as Element).visitChildren(rebuild);
}
Widget build(BuildContext context) {
if(_refreshAllChildrenAfterWakeup == true) {
_refreshAllChildrenAfterWakeup = false;
_rebuildAllChildren(context);
}
//...
}

Extracting Class Members like Widget Builders to a Different File?

In developing some of the screens for my flutter app, I regularly need to dynamically render widgets based on the state of the screen. For circumstances where it makes sense to create a separate widget and include it, I do that.
However, there are many use cases where what I need to render is not fit for a widget, and leverages existing state from the page. Therefore I use builder methods to render the appropriate widgets to the page. As anyone who uses Flutter knows, that can lead to lengthy code where you need to scroll up/down a lot to get to what you need to work on.
For better maintainability, I would love to move those builder methods into separate files, and then just include them. This would make it much easier to work on specific code widgets rendered and make the screen widget much cleaner.
But I haven't found a proper way to extract that dynamic widget code, which makes use of state, calls to update state, etc. I'm looking for a type of "include" file that would insert code into the main screen and render as if it's part of the core code.
Is this possible? How to achieve?
With the introduction of extension members, I came across this really neat way of achieving exactly what your described!
Say you have a State class defined like this:
class MyWidgetState extends State<MyWidget> {
int cakes;
#override
void initState() {
super.initState();
cakes = 0;
}
#override
Widget build(BuildContext context) {
return Builder(
builder: (context) => Text('$cakes'),
);
}
}
As you can see, there is a local variable cakes and a builder function. The very neat way to extract this builder now is the following:
extension CakesBuilderExtension on MyWidgetState {
Widget cakesBuilder(BuildContext context) {
return Text('$cakes');
}
}
Now, the cakes member can be accessed from the extension even if the extension is placed in another file.
Now, you would update your State class like this (the builder changed):
class MyWidgetState extends State<MyWidget> {
int cakes;
#override
void initState() {
super.initState();
cakes = 0;
}
#override
Widget build(BuildContext context) {
return Builder(
builder: cakesBuilder,
);
}
}
The cakesBuilder can be referenced from MyWidgetState, even though it is only declared in the CakesBuilderExtension!
Note
The extension feature requires Dart 2.6. This is not yet available in the stable channel, but should be around the end of 2019 I guess. Thus, you need to use the dev or master channels: flutter channel dev or flutter channel master and update the environment constraint in your pubspec.yaml file:
environment:
sdk: '>=2.6.0-dev.8.2 <3.0.0'

How to call into a flutter widget's state from the widget

I'm new to dart/flutter and having a little trouble getting my head around communication patterns.
One reoccurring problem is that I keep looking to expose a public method on a widget so it can be called by other widgets.
The problem is with stateful widgets. In these cases, I need to call down to the widgets state to do the actual work.
The problem is that the widget doesn't have a copy of the state.
I have been saving a copy of the state in the widget but of course this throws a warning as it makes the widget mutable.
Let me give a specific example:
I have a specialised menu which can have a set of menu items.
Each are stateful.
When the menu is closing it needs to iterate over the list of menu items that it owns and tell each one to hide (the menu items are not visually contained within the menu so hiding the menu doesn't work).
So the menu has the following code:
class Menu{
closeMenu() {
for (var menuItem in menuItems) {
menuItem.close();
}
}
So that works fine, but of course in the MenuItem class I need to:
class MenuItem {
MenuItemState state;
close()
{
state.close();
}
But of course having the state object stored In the MenuItem is a problem given that MenuItem is meant to be immutable. (It is only a warning so the code works, but its clearly not the intended design pattern).
I could do with seeing more of your code to get a better idea of how to solve your specific issue but it appears that the Flutter documentation will help you in some regard, specifically the section on Lifting state up:
In Flutter, it makes sense to keep the state above the widgets that use it.
Why? In declarative frameworks like Flutter, if you want to change the UI, you have to rebuild it.
…it’s hard to imperatively change a widget from outside, by calling a method on it. And even if you could make this work, you would be fighting the framework instead of letting it help you.
It appears you're trying to fight the framework in your example and that you were correct to be apprehensive about adding public methods to your Widgets. What you need to do is something closer to what's detailed in the documentation (which details all of the new classes etc you'll see below). I've put a quick example together based on this and the use of Provider which makes this approach to state management easy. Here's a Google I/O talk from this year encouraging its use.
void main() {
runApp(
ChangeNotifierProvider(
builder: (context) => MenuModel(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
…
// call this when the menu is closed
void onMyMenuClosed(BuildContext context) {
var menuModel = getMyMenuModel(context);
menuModel.hideMenuItems();
}
}
class MenuModel extends ChangeNotifier {
bool _displayItems = false;
void hideMenuItems() {
_displayItems = false;
notifyListeners();
}
void showMenuItems() {
_displayItems = true;
notifyListeners();
}
}
Calling hideMenuItems() makes a call to notifyListeners() that'll do just that; notify any listeners of a change which in turn prompts a rebuild of the Widget/s you wrap in a Consumer<MenuModel> Now, when the Widget that displays the menu is rebuilt, it just grabs the appropriate detail from the MenuModel class - the one source of truth for the state. This reduces the number of code paths you'd otherwise have to deal with to one and makes it far easier to see what's happening when you make further changes.
#override
Widget build(BuildContext context) {
return Consumer<MenuModel>(
builder: (context, menuModel, child) {
return menuModel._displayItems() ? MenuItemsWidget() : Container();
},
);
}
I recommend you read the entire page on state management.

Fully restart(discard and recreate) the whole flutter app programmatically [duplicate]

In production mode, is there a way to force a full restart of the application (I am not talking about a hot reload at development time!).
Practical use cases:
At initialization process the application detects that there is no network connection. The lack of network connectivity might have prevented a correct start up (e.g. loading of external resource such as JSON files...).
During the initial handshaking, new versions of some important resources need to be downloaded (kind of update).
In both use cases, I would like the application to proceed with a full restart, rather than having to build a complex logic at the ApplicationState level.
You could wrap your whole app into a statefulwidget. And when you want to restart you app, rebuild that statefulwidget with a child that possess a different Key.
This would make you loose the whole state of your app.
import 'package:flutter/material.dart';
void main() {
runApp(
RestartWidget(
child: MaterialApp(),
),
);
}
class RestartWidget extends StatefulWidget {
RestartWidget({this.child});
final Widget child;
static void restartApp(BuildContext context) {
context.findAncestorStateOfType<_RestartWidgetState>().restartApp();
}
#override
_RestartWidgetState createState() => _RestartWidgetState();
}
class _RestartWidgetState extends State<RestartWidget> {
Key key = UniqueKey();
void restartApp() {
setState(() {
key = UniqueKey();
});
}
#override
Widget build(BuildContext context) {
return KeyedSubtree(
key: key,
child: widget.child,
);
}
}
In this example you can reset your app from everywhere using RestartWidget.restartApp(context).
The flutter_phoenix package is based on Rémi Rousselet's answer, making it even simpler.
void main() {
runApp(
Phoenix(
child: App(),
),
);
}
Then when you need to restart the app, just call:
Phoenix.rebirth(context);
I developed the restart_app plugin to restart the whole app natively.
Update:
For anyone who get this exception:
MissingPluginException(No implementation found for method restartApp on channel restart)
Just stop and rebuild the app.
You can also use the runApp(new MyWidget) function to do something similar
This is what this function does:
Inflate the given widget and attach it to the screen.
The widget is given constraints during layout that force it to fill the entire screen. If you wish to align your widget to one side of the screen (e.g., the top), consider using the Align widget. If you wish to center your widget, you can also use the Center widget
Calling runApp again will detach the previous root widget from the screen and attach the given widget in its place. The new widget tree is compared against the previous widget tree and any differences are applied to the underlying render tree, similar to what happens when a StatefulWidget rebuilds after calling State.setState.
https://docs.flutter.io/flutter/widgets/runApp.html
So simple package: flutter_restart
dependencies:
flutter_restart: ^0.0.3
to use:
void _restartApp() async {
FlutterRestart.restartApp();
}
I just want to add Regarding I have Tried #Remi answer which works great on most of the cases to restart the app. The only problem with the answer is that some things if you are doing Navigation route extensively you probably go to a state which It gives you an error like,
The method 'restartApp' was called on null.
To resolve this error you have to know the Context and use Navigator.of(context).pop(); multiples times back. For me, the solution is that just go to the initial route. It will inject all the states from a new. Where you want to restart just add this Line.
Navigator.pushNamedAndRemoveUntil(context,'/',(_) => false);
If you want to only restart a specific widget then the Remi solution is awesome. Thanks for the solution Remi though. It help me understand states in flutter.
I have found Hossein's restart_app package also pretty useful for native restarts (not only on Flutter level).
To everyone having the MissingPluginException error, just reinstall the app again on the device, means that hot reload won't work. The app has native methods which need to compiled in the Android/iOS App.
I wanted to restart my app after logout.
so I used https://pub.dev/packages/flutter_phoenix (flutter phoenix).
It worked for me.
Install flutter_phoenix by running this command on your terminal inside your flutter app directory.
$ flutter pub add flutter_phoenix
Import it inside your "main.dart".
Wrap your root widget inside Phoenix.
runApp(
Phoenix(
child: MyApp()
));
Now you can call this wherever you want to restart your app :-
Phoenix.rebirth(context)
Note: flutter_phoenix does not restart the app on OS level, it only restarts the app on app level.
Thecnically this is not a restart but it will work for most of the scenarios:
// Remove any route in the stack
Navigator.of(context).popUntil((route) => false);
// Add the first route. Note MyApp() would be your first widget to the app.
Navigator.push(
context,
CupertinoPageRoute(builder: (context) => const MyApp()),
);
Follow the steps-
Go to your terminal and type in the following:
flutter pub add flutter_restart
This will update some dependencies in pubspec.yaml file.
Import the following package in whichever file you want to implement the restart code-
import 'package:flutter_restart/flutter_restart.dart';
Create a void function
void _restartApp() async {
await FlutterRestart.restartApp();
}
Write this wherever you want to start the app-
_restartApp();
I tried the above suggested methods and none of them worked and i was using getx.
so i ended up modified the accepted answer with a delay as a workaround and it works now.
class RestartAppWidget extends StatefulWidget {
RestartAppWidget({this.child});
final Widget child;
static void restartApp(BuildContext context) {
context.findAncestorStateOfType<_RestartAppWidgetState>().restartApp();
}
#override
_RestartAppWidgetState createState() => _RestartAppWidgetState();
}
class _RestartAppWidgetState extends State<RestartAppWidget> {
bool restarting = false;
void restartApp() async {
restarting = true; // restart variable is set to true
setState(() {});
Future.delayed(Duration(milliseconds: 300)).then((value) {
setState(() {
restarting = false; //restart variable is set to false
});
});
// setState(() {
// key = UniqueKey();
// });
}
#override
Widget build(BuildContext context) {
if (restarting) {
return SizedBox(); //an empty Sizedbox is displayed for 300 milliseconds you can add a loader if you want
}
return SizedBox(
child: widget.child,
);
}
}`
wrap the root widget with RestartAppWidget
runApp(RestartAppWidget(
child: MyApp(),
))
you can use this code to restart the app at flutter level
RestartAppWidget.restartApp(Get.context);

How to force a Flutter application restart (in production mode)?

In production mode, is there a way to force a full restart of the application (I am not talking about a hot reload at development time!).
Practical use cases:
At initialization process the application detects that there is no network connection. The lack of network connectivity might have prevented a correct start up (e.g. loading of external resource such as JSON files...).
During the initial handshaking, new versions of some important resources need to be downloaded (kind of update).
In both use cases, I would like the application to proceed with a full restart, rather than having to build a complex logic at the ApplicationState level.
You could wrap your whole app into a statefulwidget. And when you want to restart you app, rebuild that statefulwidget with a child that possess a different Key.
This would make you loose the whole state of your app.
import 'package:flutter/material.dart';
void main() {
runApp(
RestartWidget(
child: MaterialApp(),
),
);
}
class RestartWidget extends StatefulWidget {
RestartWidget({this.child});
final Widget child;
static void restartApp(BuildContext context) {
context.findAncestorStateOfType<_RestartWidgetState>().restartApp();
}
#override
_RestartWidgetState createState() => _RestartWidgetState();
}
class _RestartWidgetState extends State<RestartWidget> {
Key key = UniqueKey();
void restartApp() {
setState(() {
key = UniqueKey();
});
}
#override
Widget build(BuildContext context) {
return KeyedSubtree(
key: key,
child: widget.child,
);
}
}
In this example you can reset your app from everywhere using RestartWidget.restartApp(context).
The flutter_phoenix package is based on Rémi Rousselet's answer, making it even simpler.
void main() {
runApp(
Phoenix(
child: App(),
),
);
}
Then when you need to restart the app, just call:
Phoenix.rebirth(context);
I developed the restart_app plugin to restart the whole app natively.
Update:
For anyone who get this exception:
MissingPluginException(No implementation found for method restartApp on channel restart)
Just stop and rebuild the app.
You can also use the runApp(new MyWidget) function to do something similar
This is what this function does:
Inflate the given widget and attach it to the screen.
The widget is given constraints during layout that force it to fill the entire screen. If you wish to align your widget to one side of the screen (e.g., the top), consider using the Align widget. If you wish to center your widget, you can also use the Center widget
Calling runApp again will detach the previous root widget from the screen and attach the given widget in its place. The new widget tree is compared against the previous widget tree and any differences are applied to the underlying render tree, similar to what happens when a StatefulWidget rebuilds after calling State.setState.
https://docs.flutter.io/flutter/widgets/runApp.html
So simple package: flutter_restart
dependencies:
flutter_restart: ^0.0.3
to use:
void _restartApp() async {
FlutterRestart.restartApp();
}
I just want to add Regarding I have Tried #Remi answer which works great on most of the cases to restart the app. The only problem with the answer is that some things if you are doing Navigation route extensively you probably go to a state which It gives you an error like,
The method 'restartApp' was called on null.
To resolve this error you have to know the Context and use Navigator.of(context).pop(); multiples times back. For me, the solution is that just go to the initial route. It will inject all the states from a new. Where you want to restart just add this Line.
Navigator.pushNamedAndRemoveUntil(context,'/',(_) => false);
If you want to only restart a specific widget then the Remi solution is awesome. Thanks for the solution Remi though. It help me understand states in flutter.
I have found Hossein's restart_app package also pretty useful for native restarts (not only on Flutter level).
To everyone having the MissingPluginException error, just reinstall the app again on the device, means that hot reload won't work. The app has native methods which need to compiled in the Android/iOS App.
I wanted to restart my app after logout.
so I used https://pub.dev/packages/flutter_phoenix (flutter phoenix).
It worked for me.
Install flutter_phoenix by running this command on your terminal inside your flutter app directory.
$ flutter pub add flutter_phoenix
Import it inside your "main.dart".
Wrap your root widget inside Phoenix.
runApp(
Phoenix(
child: MyApp()
));
Now you can call this wherever you want to restart your app :-
Phoenix.rebirth(context)
Note: flutter_phoenix does not restart the app on OS level, it only restarts the app on app level.
Thecnically this is not a restart but it will work for most of the scenarios:
// Remove any route in the stack
Navigator.of(context).popUntil((route) => false);
// Add the first route. Note MyApp() would be your first widget to the app.
Navigator.push(
context,
CupertinoPageRoute(builder: (context) => const MyApp()),
);
Follow the steps-
Go to your terminal and type in the following:
flutter pub add flutter_restart
This will update some dependencies in pubspec.yaml file.
Import the following package in whichever file you want to implement the restart code-
import 'package:flutter_restart/flutter_restart.dart';
Create a void function
void _restartApp() async {
await FlutterRestart.restartApp();
}
Write this wherever you want to start the app-
_restartApp();
I tried the above suggested methods and none of them worked and i was using getx.
so i ended up modified the accepted answer with a delay as a workaround and it works now.
class RestartAppWidget extends StatefulWidget {
RestartAppWidget({this.child});
final Widget child;
static void restartApp(BuildContext context) {
context.findAncestorStateOfType<_RestartAppWidgetState>().restartApp();
}
#override
_RestartAppWidgetState createState() => _RestartAppWidgetState();
}
class _RestartAppWidgetState extends State<RestartAppWidget> {
bool restarting = false;
void restartApp() async {
restarting = true; // restart variable is set to true
setState(() {});
Future.delayed(Duration(milliseconds: 300)).then((value) {
setState(() {
restarting = false; //restart variable is set to false
});
});
// setState(() {
// key = UniqueKey();
// });
}
#override
Widget build(BuildContext context) {
if (restarting) {
return SizedBox(); //an empty Sizedbox is displayed for 300 milliseconds you can add a loader if you want
}
return SizedBox(
child: widget.child,
);
}
}`
wrap the root widget with RestartAppWidget
runApp(RestartAppWidget(
child: MyApp(),
))
you can use this code to restart the app at flutter level
RestartAppWidget.restartApp(Get.context);