Flutter - run a function every time the page changes - events

I want to run a function every time the page changes in my Flutter application.
Ideally, I don't want to call this function in initState of every page, as sometimes people can forget to add the call in a new page.
Think of it as middleware - be basically before the page loads etc, some code needs to run.
Updated code for review
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:myapp/pages/login_page.dart';
import 'package:myapp/pages/dashboard_page.dart';
import 'package:myapp/styles/constants.dart';
import 'package:myapp/services/auth_service.dart';
Future<void> main() async {
// create a auth service instance
AuthService authService = AuthService(secureStorage: FlutterSecureStorage());
bool isLoggedIn = await authService.isUserLoggedIn();
// run the app
runApp(MyApp(
isLoggedIn: isLoggedIn,
));
}
class MyApp extends StatefulWidget {
final bool isLoggedIn;
MyApp({this.isLoggedIn});
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with RouteAware {
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
#override
void didChangeDependencies() {
super.didChangeDependencies();
routeObserver.subscribe(this, ModalRoute.of(context));
}
#override
void dispose() {
routeObserver.unsubscribe(this);
super.dispose();
}
#override
void didPush() {
print('didPush');
}
#override
void didPopNext() {
print('didPopNext');
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'App NAME',
theme: ThemeData(
primarySwatch: Colors.green,
primaryColor: kPrimeColour,
),
home: widget.isLoggedIn == true ? DashboardPage() : LoginPage(),
navigatorObservers: [routeObserver],
);
}
}

You can use implementation method didChangeDependencies this function called after initState,
flutter doc :
Subclasses rarely override this method because the framework always calls build after a dependency changes. Some subclasses do override this method because they need to do some expensive work (e.g., network fetches) when their dependencies change, and that work would be too expensive to do for every build.
Link
#override
void didChangeDependencies() {
super.didChangeDependencies();
// set your stuff here
}

You can use inheritance
abstract class MyState<T extends StatefulWidget> extends State {
#override
void initState() {
super.initState();
//YOUR CHANGE PAGE METHOD HERE
}
}
class YellowBird extends StatefulWidget {
const YellowBird({ Key key }) : super(key: key);
#override
_YellowBirdState createState() => _YellowBirdState();
}
class _YellowBirdState extends MyState<YellowBird> {
#override
Widget build(BuildContext context) {
return Container(color: const Color(0xFFFFE306));
}
}

Related

Webview with pull down refresh

I'm starting in Flutter and I don't know how to refresh the Webview with the scroll down.
This is my current code. The Webview is displayed normally, but the scroll down doesn't refresh.
I already tried using inappwebview and pull_to_refresh, but nothing works.
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:flutter_launcher_icons/constants.dart';
import 'package:flutter/widgets.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'WebView Example',
theme: ThemeData.light(),
home: WebViewExample(),
);
}
}
class WebViewExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: WebView(
initialUrl: 'https://stackoverflow.com',
javascriptMode: JavascriptMode.unrestricted,
),
);
}
}
Anyone who can provide a practical, working example of this implementation? Any help will be appreciated.

Flutter WidgetsBindingObserver's didChangeAppLifecycleState not being called on Windows Desktop

The print functions should be called according to the lifecycle transitions, but none of them are being called. To test this, I'm running the app in debug mode and moving it to the background/foreground (i.e. changing to another app and then returning to this app).
What am I doing wrong?
import 'package:flutter/material.dart';
class StopwatchVw extends StatefulWidget {
const StopwatchVw({Key? key}) : super(key: key);
#override _StopwatchVwState createState() => _StopwatchVwState();
}
class _StopwatchVwState extends State<StopwatchVw> with WidgetsBindingObserver {
#override
void initState() {
super.initState();
WidgetsBinding.instance!.addObserver(this);
}
#override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) {
print('Changed');
switch (state) {
case AppLifecycleState.inactive:
print("Inactive");
break;
case AppLifecycleState.paused:
print("Paused");
break;
case AppLifecycleState.resumed:
print("Resumed");
break;
case AppLifecycleState.detached:
print("Suspending");
break;
}
}
#override
void dispose() {
WidgetsBinding.instance!.removeObserver(this);
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(body: Text('HEY'));
}
This is my main.dart:
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:clocker/stopwatch_vw.dart';
import 'package:flutter/material.dart';
import 'package:flutter_acrylic/flutter_acrylic.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Window.initialize();
await Window.hideWindowControls();
runApp(const MyApp());
doWhenWindowReady(() {
const initialSize = Size(350, 200);
appWindow
..size = initialSize
..minSize = initialSize
..maxSize = initialSize
..alignment = Alignment.bottomRight
..show();
});
Window.setEffect(
effect: WindowEffect.acrylic,
color: const Color.fromARGB(29, 250, 250, 227),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MoveWindow(child: const StopwatchVw())
);
}
}
Flutter currently doesn't support LifeCycle events on the desktop, But there is an existing issue which is being tracked here #30735.
Currently, you can determine the status of your application with the window_manager package
class Windows extends StatefulWidget {
const Windows({super.key});
#override
State<Windows> createState() => _WindowsState();
}
class _WindowsState extends State<Windows> with WindowListener {
#override
void onWindowClose() {
// do something
}
#override
void onWindowFocus() {
// do something
}
#override
void onWindowMinimize() {
// do something
}
#override
void initState() {
windowManager.addListener(this);
super.initState();
}
#override
void dispose() {
windowManager.removeListener(this);
super.dispose();
}

How can I control animations in Flutter from outside?

Passing state down to widgets is easy. I have a StatefulWidget that contains an animation with its controller. I need to be able to trigger the animation from another widget higher in my widget tree.
My MainApp should trigger the animation using a button.
As I understand AnimationController only has an imperative API. I can call controller.forward() or controller.reverse(). But to do this I need to expose the controller to my MainApp.
What I currently do is to keep a global variable of my state around.
class MainApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
...
body: new LogoWidget(),
);
}
_startAnimation() {
_state.restartAnimation();
}
}
_LogoWidgetState _state; // yuk!
class LogoWidget extends StatefulWidget {
_LogoWidgetState createState() {
_state = _LogoWidgetState();
return _state;
}
}
class _LogoWidgetState extends State<LogoWidget>
with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
restartAnimation() {
controller.value == 1.0 ? controller.reverse() : controller.forward();
}
...
}
(full sourcecode here)
What is a better way to deal with this?
You don't need _LogoWidgetState _state; // yuk! out in the middle of nowhere, but you can try:
create LogoWidget _myBody = LogoWidget(), and use that for your body:
similarily, apply with final _LogoWidgetState _state = _LogoWidgetState()
then call it as _myBody._state.restartAnimation()
Your sample, modified:
class MainApp extends StatelessWidget {
LogoWidget _myBody = LogoWidget(); //<---
#override
Widget build(BuildContext context) {
return new Scaffold(
...
body: _myBody, //<---
);
}
_startAnimation() {
_myBody._state.restartAnimation(); //<---
}
}
class LogoWidget extends StatefulWidget {
final _LogoWidgetState _state = _LogoWidgetState(); //<---
_LogoWidgetState createState() {
return _state;
}
}
But if you think _myBody._state.restartAnimation() is too long, you can shorten it with:
class LogoWidget extends StatefulWidget {
final _LogoWidgetState _state = _LogoWidgetState(); //<---
void restartAnimation() { //<---
_state.restartAnimation();
}
_LogoWidgetState createState() {
return _state;
}
}
Then just use _myBody.restartAnimation()
Here's some relevant posts:
call method in one stateful widget from another stateful widget - Flutter
Flutter: Call a function on a child widget's state

GWT, how to fire event from widget or composite using EventBus from HandlerManager

I have widget. I would like to fire an event as follow:
fireEvent(new IndicatorStartEvent("Message"));
But it dosn't work.
Normally I use Presenter for this (GWTP), but now I just would like to have regular widget:
public class FileUploadWidget extends Composite {
MaterialFileUploader uploader = new MaterialFileUploader();
#Inject
public FileUploadWidget(String triggerId, EventBus eventBus) {
super();
initWidget(uploader);
Window.alert("TEST Start");
fireEvent(new IndicatorStartEvent("Message"));
}
}
Here is event code:
public class IndicatorStartEvent extends GwtEvent<IndicatorStartEvent.IndicatorHandler> {
public static Type<IndicatorHandler> TYPE = new Type<IndicatorHandler>();
public interface IndicatorHandler extends EventHandler {
void onIndicatorProgressStart(IndicatorStartEvent event);
}
public interface IndicatorHandlers extends HasHandlers {
HandlerRegistration addStartIndicatorHandler(IndicatorHandler handler);
}
private final String message;
public IndicatorStartEvent(final String message) {
this.message = message;
}
public static Type<IndicatorHandler> getType() {
return TYPE;
}
#Override
protected void dispatch(final IndicatorHandler handler) {
handler.onIndicatorProgressStart(this);
}
#Override
public Type<IndicatorHandler> getAssociatedType() {
return TYPE;
}
public String getMessage() {
return this.message;
}
}
This is my app presenter that handle the event:
public class AppPresenter extends TabContainerPresenter<AppPresenter.MyView, AppPresenter.MyProxy> implements AppUiHandlers
, IndicatorStartEvent.IndicatorHandler {
#ProxyStandard
public interface MyProxy extends Proxy<AppPresenter> {}
public interface MyView extends TabView, HasUiHandlers<AppUiHandlers> {}
#Override
protected void onBind() {
super.onBind();
addRegisteredHandler(IndicatorStartEvent.getType(), this);
}
public void onAsyncCallFail(AsyncCallFailEvent event) {
// fireEvent is executed from: com.gwtplatform.mvp.client;PresenterWidget
fireEvent(new IndicatorStartEvent("Firing message"));
}
#Override
public void onIndicatorProgressStart(IndicatorStartEvent event) {
MaterialToast.fireToast("Indicator start: " + event.getMessage());
}
}
If I fire this event from f.e.: AppPresenter (code above), or GwtRESTY filter/callback ass follow:
class ProgressIndicatorFilter implements DispatcherFilter {
private AssistedInjectionFactory factory;
private EventBus eventBus;
#Inject
public ProgressIndicatorFilter(AssistedInjectionFactory factory, EventBus eventBus) {
this.factory = factory;
this.eventBus = eventBus;
}
#Override
public boolean filter(Method method, RequestBuilder builder) {
builder.setCallback(factory.createProgressIndicatorCallback(method));
eventBus.fireEvent(new IndicatorStartEvent("Rest-Gwt Comunication started"));
return true;
}
}
It work as expected. But in those working examples it use com.google.web.bindery.event.shared;EventBus
The firing event doesnt work from widget, where is used:
com.google.gwt.event.shared;HandlerManager;Bus class. This class Bus extends com.google.web.bindery.event.shared.SimpleEventBus which extends the proper EventBus class from com.google.web.bindery.event.shared;EventBus.
So the widget's method fireEvent() use other EventBus.
Can anyone help me with this?
I've red official and this instruction:
http://blog.arcbees.com/2015/04/01/gwt-platform-event-best-practices-revisited/ but no luck so far. Please help.
It does not work because your FileUploadWidget uses it's own EventBus and not GWTP one that is also used in all of your Presenters.
There are two solutions:
Don't use fireEvent(new IndicatorStartEvent("Message")) but use eventBus.fireEvent(new IndicatorStartEvent("Message")) on the injected EventBus inside of your Widget.
Add the IndicatorStartEvent handler to your FileUploadWidget directly instead of using addRegisteredHandler on your Presenter.
I prefer solution 2:
public class FileUploadWidget extends Composite {
MaterialFileUploader uploader = new MaterialFileUploader();
#Inject
public FileUploadWidget(String triggerId) {
super();
initWidget(uploader);
Window.alert("TEST Start");
fireEvent(new IndicatorStartEvent("Message"));
}
}
In the Presenter or to be precise the View which uses your FileUploadWidget, you add a handler directly to the FileUploadWidget:
public class UploadView extends ViewWithUiHandlers<UploadUiHandlers> implements UploadPresenter.MyView,IndicatorStartEvent.IndicatorHandler {
#UiField
FileUploadWidget uploadWidget;
#Inject
public UploadView(final Binder binder) {
widget = binder.createAndBindUi(this);
uploadWidget.addHandler(new IndicatorStartEvent.Handler(),this);
}
public void onIndicatorProgressStart(IndicatorStartEvent event) {
MaterialToast.fireToast("Indicator start: " + event.getMessage());
}
}

JMeter Plugin - How to Listen to TestState

I am working on developing a JMeter plugin. I'm trying to create an AbstractVisualizer that is capable of monitoring the current test state. However, implementing the TestStateListener doesn't seem to be working.
I'm testing this by creating a basic listener that has a login to output arbitrary info to JMeter's logging console. When a sample is sent through the Add function, a line is sent to the console. But nothing is ever triggered on the various TestState functions. Is there something more structural I'm missing?
public class TestListener extends AbstractVisualizer
implements TestStateListener
{
private static final Logger log = LoggingManager.getLoggerForClass();
#Override
public void add(SampleResult arg0) {
log.info("add");
}
#Override
public void clearData() {
// TODO Auto-generated method stub
}
#Override
public String getStaticLabel()
{
return "Test Listener";
}
#Override
public String getLabelResource() {
return null;
}
#Override
public void testEnded() {
log.info("Test Ended");
}
#Override
public void testEnded(String arg0) {
log.info("Test Ended");
}
#Override
public void testStarted() {
log.info("Test started");
}
#Override
public void testStarted(String arg0) {
log.info("Test started");
}
}
I'm not sure how to do it in 1 class. I have 2 classes:
The UI:
public class MonitorGui extends AbstractListenerGui
{
// ...
#Override
public TestElement createTestElement()
{
TestElement element = new Monitor();// <-- this is the backend
modifyTestElement(element);
return element;
}
// ...
}
And then the backend goes like this:
public class Monitor extends AbstractListenerElement
implements SampleListener,
Clearable, Serializable,
TestStateListener, Remoteable,
NoThreadClone
{
private static final String TEST_IS_LOCAL = "*local*";
// ...
#Override
public void testStarted()
{
testStarted(TEST_IS_LOCAL);
}
#Override
public void testEnded()
{
testEnded(TEST_IS_LOCAL);
}
#Override
public void testStarted(String host)
{
// ...
}
// ...
}
You may not need to implement SampleListener like I do, but probably other things are quite similar.
I based that implementation on a built-in pair of ResultSaverGui and ResultCollector which are the components that are saving results into the file(s) for Simple Data Writer, Summary Report and so on.

Resources