How can I stop Microsoft Teams from opening links in Microsoft Edge and make it open them in Google Chrome instead?
Click the title of the article to see the solution
Click the title of the article to see the solution
In a recent project, I ran into an issue where I needed to refresh a page when the user navigated back to it. After searching on google and asking ChatGPT, I found this simple and clean solution.
RouteObserver
is part of the Flutter framework that helps you track navigation events in your app. It allows you to listen to changes in the route stack, such as when a route is pushed or popped. This is incredibly useful for scenarios where you need to refresh or update your UI based on navigation.
First, I created a UserSession
class to hold a singleton instance of RouteObserver
. Here’s the gist:
// user_session.dart
import 'package:flutter/material.dart';
class UserSession {
static final UserSession _instance = UserSession._internal();
static final routeObserver = RouteObserver<ModalRoute>();
UserSession._internal();
}
By doing this, I could easily access routeObserver
from anywhere in my app. My UserSession also holds other user session data, maybe a bettway is to create a separate file for RouteObserver. But for now, we just use the UserSession class.
Next, I needed to register this RouteObserver
with my app’s navigator. I did this in the main app widget:
// main.dart
import 'package:flutter/material.dart';
import 'package:happy_notes/screens/account/user_session.dart';
class HappyNotesApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: const InitialPage(),
navigatorObservers: [UserSession.routeObserver],
);
}
}
By adding UserSession.routeObserver
to navigatorObservers
, we’re now tracking route changes across the entire app.
To use RouteObserver
, I extended my state classes with RouteAware
and subscribed to the routeObserver
in the initState
method. Here’s an example from the Memories
screen:
// memories.dart
import 'package:flutter/material.dart';
import '../account/user_session.dart';
class MemoriesState extends State<Memories> with RouteAware {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
UserSession.routeObserver.subscribe(this, ModalRoute.of(context)!);
});
}
@override
void didPopNext() {
// Called when the top route has been popped off, and this route shows up
_fetchMemories();
setState(() {});
}
@override
void dispose() {
UserSession.routeObserver.unsubscribe(this);
super.dispose();
}
void _fetchMemories() {
// Logic to refresh memories
}
}
Here’s what’s happening:
initState
, the screen subscribes to routeObserver
.didPopNext
method is triggered when the screen becomes visible again after another screen is popped off. This is where I refreshed the screen's data by calling _fetchMemories()
.dispose
method to avoid memory leaks.Using RouteObserver
in Flutter allows you to manage your app’s navigation state effectively. By listening to route changes, you can ensure that your UI stays in sync with user actions, providing a seamless experience.
Managing time zones can be tricky, especially in regions that observe daylight saving time (DST). Using the right time zone identifiers can simplify your work by automatically adjusting for these changes. Here’s a quick guide to best practices for handling time zones, using examples from New Zealand and Australia.
The Problem with Fixed Time Zones Using fixed time zones like NZST (New Zealand Standard Time) typically doesn't account for daylight saving changes, potentially leading to incorrect time calculations during daylight saving periods. However, certain platforms, such as .NET, which handles these transitions internally, giving the correct local time. This behavior might not be consistent across different platforms and systems.
The best practice is to use time zone identifiers that handle both standard and daylight saving times automatically. These identifiers switch between standard time and daylight saving time based on the current date, keeping your times accurate year-round. For example:
Pacific/Auckland
.Australia/Sydney
.America/New_York
for Eastern Time, America/Los_Angeles
for Pacific Time, etc.These identifiers know when to transition between standard time and daylight saving time, ensuring your applications reflect the correct local time.
In the HappyNotes project, I encountered a perplexing issue with the NoteModel
not updating as expected across flutter widgets. Despite initializing NoteModel
with initial values and using ChangeNotifierProvider
, the state changes weren’t reflecting in the model when I try to collect data back. This article outlines the troubleshooting process and solution, which can be a valuable guide for other developers facing similar challenges.
In HappyNotes project, the NewNote
widget is where users could create notes. Each note had properties like isPrivate
and isMarkdown
, managed through a NoteModel
using ChangeNotifier
. Here’s a simplified version of the relevant parts of my setup:
class NoteModel extends ChangeNotifier {
bool isPrivate;
bool isMarkdown;
NoteModel({this.isPrivate = true, this.isMarkdown = false});
set isPrivate(bool value) {
_isPrivate = value;
notifyListeners();
}
set isMarkdown(bool value) {
_isMarkdown = value;
notifyListeners();
}
}
The NewNote
widget utilized ChangeNotifierProvider
to provide the NoteModel
to its children:
class NewNote extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => NoteModel(),
child: Scaffold(
appBar: AppBar(title: Consumer<NoteModel>(
builder: (context, noteModel, child) {
return Text(noteModel.isPrivate ? 'Private Note' : 'Public Note');
},
)),
body: NoteEditor(),
),
);
}
}
After setting the initial values for isPrivate
and isMarkdown
, later changes to these properties weren’t being reflected in the model when I retrieve the status data when saving a note . I tried various approaches, including manually calling notifyListeners
and using setState
within event handlers, but nothing solved the issue.
Double Check Initialization:
First, I verified that NoteModel
was being initialized correctly with the ChangeNotifierProvider
. The initial values were correct, but subsequent changes weren’t propagating.
Use of Consumer
:
I ensured that widgets depending on NoteModel
used Consumer
to listen for changes:
Consumer<NoteModel>(
builder: (context, noteModel, child) {
return Switch(
value: noteModel.isPrivate,
onChanged: (value) {
noteModel.isPrivate = value;
},
);
},
);
Finally I found that having multiple ChangeNotifierProviders
at different levels of the widget tree, is the root cause that leads to this issue. I shouldn't initialize the same model twice at the root level and at the page level.
The breakthrough came when we adjusted our main application setup to use a single ChangeNotifierProvider
for NoteModel
:
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => NoteModel(),
child: MyApp(),
),
);
}
In NewNote
, I removed the redundant ChangeNotifierProvider
and directly used the globally provided NoteModel
:
class NewNote extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Consumer<NoteModel>(
builder: (context, noteModel, child) {
return Text(noteModel.isPrivate ? 'Private Note' : 'Public Note');
},
),
),
body: NoteEditor(),
);
}
}
By centralizing NoteModel
provisioning, state changes propagated correctly across the app. This resolved the issue, proving that a single ChangeNotifierProvider
is the key for consistent state management in Flutter.
ChangeNotifierProvider
: Ensure a single source of truth for state by providing your model at a high level in the widget tree.Consumer
for Efficient Updates: Use Consumer
to automatically rebuild parts of the UI that depend on the model, reducing manual state management.Provider
is a powerful state management solution in Flutter that allows for efficient and organized management of app state. In this guide, we'll explore some best practices for using Provider
to keep your Flutter apps clean and maintainable.
Provider
?Provider
offers a simple, scalable way to handle state management in Flutter apps. It's ideal for sharing and managing state across widgets, especially in large applications where state needs to be accessed from various parts of the app.
MultiProvider
for Multiple ModelsWhen your app requires multiple state models, initializing each one individually can become cumbersome. Instead, use MultiProvider
to group your models together efficiently. This keeps your main function neat and ensures that all your models are readily available throughout the app.
Example:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:your_app/models/note_model.dart';
import 'package:your_app/models/user_model.dart';
import 'package:your_app/screens/home_screen.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => NoteModel()),
ChangeNotifierProvider(create: (context) => UserModel()),
],
child: const YourApp(),
),
);
}
In this setup, both NoteModel
and UserModel
are initialized at the root level, making them accessible throughout your app.
Avoid providing all your models at the root level unless they are needed throughout the entire app. For state that is only relevant to specific sections or widgets, provide the ChangeNotifier
closer to where it's needed. This reduces unnecessary rebuilds and makes your app more efficient.
Example:
class SomeFeatureScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => SomeFeatureModel(),
child: SomeFeatureWidget(),
);
}
}
In this example, SomeFeatureModel
is provided only for SomeFeatureScreen
, ensuring it's scoped to the relevant part of the app.
Ensure each ChangeNotifier
is provided only once at the appropriate level to avoid duplication. Multiple instances of the same model can lead to inconsistent state and unexpected behavior.
Example:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => UserModel()),
// Avoid duplicating UserModel in nested widgets
],
child: MaterialApp(
home: HomeScreen(),
),
);
}
}
Provider
for Static DataNot all models need to be ChangeNotifier
. For data that doesn’t change or doesn’t need to notify listeners, use Provider
. This is useful for static data or configurations.
Example:
Provider(create: (context) => SomeStaticData()),
Use context.watch<T>()
to get notified of changes and rebuild when the state changes, or context.read<T>()
to read the value without rebuilding. This ensures that your widgets only rebuild when necessary, keeping your app performance optimal.
Example:
class ExampleWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final noteModel = context.watch<NoteModel>();
final userModel = context.read<UserModel>();
return Column(
children: [
Text('Note is private: ${noteModel.isPrivate}'),
Text('User name: ${userModel.name}'),
],
);
}
}
Using Provider
effectively involves:
MultiProvider
: This keeps your main function clean and ensures that all necessary models are provided.Provider
for static data: This avoids unnecessary rebuilds and keeps your app efficient.