Hot reload doesn't work and need to Hot Restart Flutter - android

I'm making an app, which has a theme manager, light mode, dark mode, and system mode.
I think that the way I manage the state with redux, prevent me to reload the app with the hot reload, I explain:
If I have a scaffold with a background, from the themedata, if the thememode is currently dark, and the background color is Color(0xFF1F1F1F) (a shade of black), and I change it to Color(0xFFFFFFFF) (Pure white), then I use hot reload, it doesn't work, and I need to restart the whole app with hot restart to see changes.
The same with my theme manager, I use radiobuttons to manage it, if I change the theme to light or dark, it works fine.
if I change the mode to system, it works fine, but with the system theme mode in which the app were opened, if I change the system theme mode of my phone, it doesn't work. in this case StoreProvider.dispatch doesn't work, store.dispatch neither, I have to use the hot restart.
Here the code that can cause the problem.
main.dart:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:async_redux/async_redux.dart';
import 'package:hive/hive.dart';
import 'package:app_name/Redux/States/AppState.dart';
import 'package:app_name/UI/Screens/AppHome.dart';
import 'package:app_name/Utilities/Themes.dart';
import 'package:app_name/Utilities/functions.dart';
import 'package:app_name/Utilities/extensions.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
Box box = await initHive();
SettingsState settingsState = SettingsState(
themeMode: box.get("theme").toString().themeModeFromString,
);
Store<AppState> store = Store(
initialState: AppState(settings: settingsState),
);
runApp(App(store: store));
}
class App extends StatelessWidget {
final Store<AppState> store;
App({
required this.store,
});
Widget build(BuildContext context) {
return StoreProvider<AppState>(
store: store,
child: StoreConnector<AppState, SettingsState>(
converter: (Store<AppState> store) => store.state.settings,
builder: (BuildContext context, SettingsState settings) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Carleo',
theme: lightTheme,
darkTheme: darkTheme,
themeMode: settings.themeMode,
home: Focus(
onFocusChange: (hasFocus) =>
SystemChrome.setEnabledSystemUIOverlays([]),
autofocus: true,
descendantsAreFocusable: true,
child: AppHome(),
),
);
},
),
);
}
}
Here the file AppHome.dart:
import 'package:app_name/UI/Widgets/Radio%20buttons.dart';
import 'package:flutter/material.dart';
import 'package:carleo/UI/Screens/SelectAccess.dart';
import 'package:carleo/UI/Screens/Home.dart';
import 'package:carleo/UI/Widgets/CircularIndicator.dart';
import 'package:carleo/Utilities/Database Utilities.dart';
import 'package:app_name/Utilities/Themes.dart';
class AppHome extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ThemeGetter.primary(context),
body: ThemeRadioButton(),
);
}
}
Here Radio Buttons.dart:
import 'package:flutter/material.dart';
import 'package:async_redux/async_redux.dart';
import 'package:app_name/Redux/States/AppState.dart';
import 'package:app_name/Redux/Actions/Actions.dart';
class ThemeRadioButton extends StatelessWidget {
Widget build(BuildContext context) {
return StoreConnector<AppState, SettingsState>(
converter: (Store<AppState> store) => store.state.settings,
builder: (BuildContext context, SettingsState settings) {
return Wrap(
children: [
ListTile(
leading: Radio<ThemeMode>(
value: ThemeMode.light,
fillColor: MaterialStateProperty.all(Colors.black),
groupValue: settings.themeMode,
onChanged: (ThemeMode? mode) {
StoreProvider.dispatch<AppState>(
context,
ThemeChanger(
payload: mode ?? ThemeMode.light,
),
);
},
),
title: Text("Light"),
),
ListTile(
leading: Radio<ThemeMode>(
value: ThemeMode.dark,
fillColor: MaterialStateProperty.all(Colors.black),
groupValue: settings.themeMode,
onChanged: (ThemeMode? mode) {
StoreProvider.dispatch<AppState>(
context,
ThemeChanger(
payload: mode ?? ThemeMode.dark,
),
);
},
),
title: Text("Dark"),
),
ListTile(
leading: Radio<ThemeMode>(
value: ThemeMode.system,
fillColor: MaterialStateProperty.all(Colors.black),
groupValue: settings.themeMode,
onChanged: (ThemeMode? mode) {
StoreProvider.dispatch<AppState>(
context,
ThemeChanger(
payload: mode ?? ThemeMode.system,
),
);
},
),
title: Text("System"),
),
],
);
},
);
}
}
Here any other thing that can help:
ThemeData darkTheme = ThemeData(
backgroundColor: Color(0xFF1F1F1F),
accentColor: Color(0xFF101217),
primaryColor: Color(0xFFFFFFFF),
buttonColor: Color(0xFF0D47A1),
brightness: Brightness.dark,
);
ThemeData lightTheme = ThemeData(
backgroundColor: Color(0xFFFFFFFF),
accentColor: Color(0xFFFFFFFF),
brightness: Brightness.light,
);
class ThemeGetter {
static Color primary(BuildContext context) {
return Theme.of(context).backgroundColor;
}
static Color accent(BuildContext context) {
return Theme.of(context).accentColor;
}
static Color contrast(BuildContext context) {
return Theme.of(context).primaryColor;
}
static Color secondary(BuildContext context) {
return Theme.of(context).buttonColor;
}
}
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart' show ThemeMode;
extension StringEnumExtension on String {
ThemeMode get themeModeFromString => ThemeMode.values.firstWhere(
(e) => describeEnum(e) == this,
orElse: () => ThemeMode.system,
);
}
extension ThemeModeExtensions on ThemeMode {
String get name => describeEnum(this);
}
import 'package:flutter/material.dart' show ThemeMode;
class AppState {
final SettingsState settings;
AppState({SettingsState? settings})
: this.settings = settings ?? SettingsState();
AppState.copy({
required AppState state,
}) : this.settings = state.settings;
AppState copyWith({int? counter, SettingsState? settings}) => AppState(
settings: settings ?? this.settings,
);
#override
operator ==(Object another) =>
identical(this, another) ||
(another is AppState && this.settings == another.settings);
#override
int get hashCode => super.hashCode;
}
class SettingsState {
final ThemeMode themeMode;
SettingsState({
ThemeMode? themeMode,
}) : this.themeMode = themeMode ?? ThemeMode.system;
SettingsState.copy({
required SettingsState state,
}) : this.themeMode = state.themeMode;
SettingsState copyWith({ThemeMode? themeMode, ThemeMode? radioValue}) =>
SettingsState(
themeMode: themeMode ?? this.themeMode,
);
#override
operator ==(Object another) =>
identical(this, another) &&
(another is SettingsState && another.themeMode == this.themeMode);
#override
int get hashCode => super.hashCode;
}
import 'package:async_redux/async_redux.dart';
import 'package:flutter/material.dart' show ThemeMode;
import 'package:hive/hive.dart';
import 'package:carleo/Redux/States/AppState.dart';
import 'package:carleo/Utilities/extensions.dart';
class ThemeChanger extends ReduxAction<AppState> {
final ThemeMode payload;
ThemeChanger({
required this.payload,
});
#override
Future<AppState> reduce() async {
Box box = await Hive.openBox("Settings");
box.put("theme", payload.name);
return state.copyWith(
settings: state.settings.copyWith(
themeMode: payload,
),
);
}
}
Any help will be accepted. and thanks in advance.
EDIT:
I've noticed that, even if settings.themeMode is ThemeMode.system, and, in the store connector (the one which builds the MaterialApp), the themeData values are correct, if I use later the same color with Theme.of(context) (like I do in the background of the scaffold in AppHome), the color printed is not the correct one, is the color of the last hot restart.
I put this in the StoreConnector builder to notice that:
Brightness brightness =
SchedulerBinding.instance!.window.platformBrightness;
bool darkModeOn = brightness == Brightness.dark;
print(settings.themeMode);
switch (settings.themeMode) {
case ThemeMode.light:
print(lightTheme.primaryColor);
break;
case ThemeMode.dark:
print(darkTheme.primaryColor);
break;
case ThemeMode.system:
if (darkModeOn)
print(darkTheme.primaryColor);
else
print(lightTheme.primaryColor);
}
and simply this code to notice that the colors are different in different widgets:
print(Theme.of(context).backgroundColor);

Some code changes to the app’s main() or initState() methods might not be visible in the refreshed UI on hot-reload.
As a general rule, if the modified code is downstream of the root
widget’s build() method, then hot reload behaves as expected. However,
if the modified code won’t be re-executed as a result of rebuilding
the widget tree, then you won’t see its effects after hot reload.
This may be the reason for the change to theme data not reflected after hot-reload since it is defined in the app's main().
So from Flutter documentation
Hot reload loads code changes into the VM and re-builds the widget
tree, preserving the app state; it doesn’t rerun main() or
initState().

According to the flutter docs - hot reload is not enabled for flutter web as of 16/07/21
Flutter hot reload doc
Kia Kaha,
Mike Smith

Related

how to get notified whenever the user changes the os theme in flutter?

I am trying to get notified, whenever the user changes the theme of the operating system. I want to use Provider to accomplish that, however dart Provider needs a Stream that gives a Snapshot, whenerver somthing is changed or getsupdated. So I need to emplement or rather use a Stream, that gives me a snapshot whenever the os theme gets changed.
Here is my code. It is nothing special. But I really want to know how to get this Provider up and running with a Stream
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MaterialApp(initialRoute: '/', routes: {
'/': (context) => MainPage(),
}));
This class is a wrapper for the HomePage. It contains the Provider.
(value: brightnessStream) is a dummy value, and that is what I need to implement.
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
#override
Widget build(BuildContext context) {
return StreamProvider<Brightness>.value(
initialData: Brightness.light,
value: brightnessStream,
child: Home(),
);
}
}
In this class I am listening to the Stream, whenever the brightness changes and displying a text that shows the current theme.
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
final brightness = Provider.of<Brightness>(context);
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('App'),
),
body: Center(
child: Text(brightness.toString()),
),
);
}
}
the stream should like somethig like this.
Stream<Brightness> get brightnessStream {
// return stream of os brigtness (os theme)
}
So how is it possible?
Here's how you can set different colors for light and dark mode, the app will automatically switch if the phone is set to dark mode or light mode.
MaterialApp(
theme: ThemeData(
brightness: Brightness.light,
primaryColor: Colors.red,
),
darkTheme: ThemeData(
brightness: Brightness.dark,
// additional settings go here
),
);
You can also get the platform brightness (Brightness.light / Brightness.dark) using
WidgetsBinding.instance.window.platformBrightness
but you will have to use the WidgetsBindingObserver mixin and override the method below
#override
void didChangePlatformBrightness() {
print(WidgetsBinding.instance.window.platformBrightness); // should print Brightness.light / Brightness.dark when you switch
super.didChangePlatformBrightness(); // make sure you call this
}
and then inside the didChangePlatformBrightness you can add to your stream.
This is also duplicate.
click here to view
Thank you for your answers. I solved the Problem like this:
class Theme {
final window = WidgetsBinding.instance.window;
final _controller = StreamController<Brightness>();
Theme() {
window.onPlatformBrightnessChanged = () {
// This callback gets invoked every time brightness changes
final brightness = window.platformBrightness;
_controller.sink.add(brightness);
};
}
Stream<Brightness> get stream => _controller.stream;
}
so I built my own stream

Flutter - losing state when pressing back button to android home screen

I have an issue with flutter. I have managed to implement a basic navigation system that keeps state when you do either of the following:
switch between tabs
press the android home button and re-open the app (either by clicking on the app again or using the list of active app button (the little square at the bottom))
But if I press the back button - going back to the android homescreen I completely lose state. I have re-implemented some code to randomly generate a number and display it on the app - this way I know if I'm getting the same widget or a new one has been built.
Why do I need this? (if you're interested)
I'm creating an audio app and when I click play song, it plays. but when I click back to the home screen and let it play in the background -> then open the app again, I can play it again and have it playing twice!
Main:
import 'package:flutter/material.dart';
import 'BottomNavigationBarController.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Login',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: BottomNavigationBarController(),
);
}
}
Bottom navigation tab (BottomNavigationBarController):
import 'package:flutter/material.dart';
import 'PlaceholderWidget.dart';
class BottomNavigationBarController extends StatefulWidget {
BottomNavigationBarController({Key key}) : super(key: key);
#override
_BottomNavigationBarController createState() => _BottomNavigationBarController();
}
class _BottomNavigationBarController extends State<BottomNavigationBarController>{
int _selectedPage = 0;
List<Widget> pageList = List<Widget>();
#override
void initState() {
pageList.add(PlaceholderWidget());
pageList.add(PlaceholderWidget());
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: _selectedPage,
children: pageList,
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.phone_android),
title: Text('First Page'),
),
BottomNavigationBarItem(
icon: Icon(Icons.phone_android),
title: Text('Second Page'),
),
],
currentIndex: _selectedPage,
selectedItemColor: Colors.blue,
onTap: _onItemTapped,
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
void _onItemTapped(int index) {
setState(() {
_selectedPage = index;
});
}
}
Random number widget (PlaceholderWidget):
import 'dart:math';
import 'package:flutter/material.dart';
class PlaceholderWidget extends StatefulWidget {
PlaceholderWidget({Key key, this.color}) : super(key: key);
final Color color;
#override
_PlaceholderWidget createState() => _PlaceholderWidget();
}
class _PlaceholderWidget extends State<PlaceholderWidget> with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
return Container(
color: widget.color,
child: Text(random_num().toString()),
);
}
int random_num(){
Random random = new Random();
int randomNumber = random.nextInt(100);
return randomNumber;
}
}
Any help will be appreciated :)
I think Navigator pop deletes the widget not entirely sure. But If you want to save the current state just use navigator push don't pop. Also use named routes this will help you greatly.
Use Provider to pass state and keep a global store.
If your app will need to scale, now is a good time to start with MobX/BLoC/Redux/InheritedWidget.. etc.

Why doesn't anything show up the body of this flutter scaffold?

The class in question is invoked from another page with the line
onPressed: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) =>
ProPage(iD: bestRatedPros[index]["ID"])));
},
Where bestRatedPros is a list of maps with the variable iD for the following class -
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class ProPage extends StatefulWidget {
ProPage({Key key, this.iD}) : super(key: key);
final iD;
#override
_ProPageState createState() => _ProPageState(iD);
}
class _ProPageState extends State<ProPage> {
int iD;
_ProPageState(this.iD);
#override
void initState() {
super.initState();
}
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.amber,
extendBodyBehindAppBar: true,
appBar: AppBar(
iconTheme: IconThemeData(
color: Colors.white, //change your color here
),
elevation: 0,
backgroundColor: Colors.amber
),
body:
Text("EWFWEFEWEWFWEF",style: TextStyle(color: Colors.black))
);
}
}
The getDataFromBackend function and
all the variables associated with it was meant to be within the body. But Nothing shows up in the body no matter what it is. Even a simple Text widget doesn't. I'm only trying to pass the variable iD from one page to the other without complicating things. The Run log doesn't show any Errors or warnings.
Arun,
See below where your Text is:
Reason for that is that you specified:
extendBodyBehindAppBar: true,
on your Scaffold, so body is expanded and top part of it is hidden behind AppBar

Can I integrate tawk.to into the nav bar of my Flutter app?

I'd like to integrate my websites tawk.to chat button into my Flutter app in some way. Maybe loading a webview at all times only showing the icon? But then when it's clicked I want it to maximize over the current content, and also for the user to receive a vibration or notification when a message is received in the chat.
Here's the widget code for tawk.to:
<!--Start of Tawk.to Script-->
<script type="text/javascript">
var Tawk_API=Tawk_API||{}, Tawk_LoadStart=new Date();
(function(){
var s1=document.createElement("script"),s0=document.getElementsByTagName("script")[0];
s1.async=true;
s1.src='https://embed.tawk.to/5c8306ab101df77a8be1a645/default';
s1.charset='UTF-8';
s1.setAttribute('crossorigin','*');
s0.parentNode.insertBefore(s1,s0);
})();
</script>
<!--End of Tawk.to Script-->
I want to use tawk.to as that's what I'm using on my website right now aswell, having 2 different chat systems would make everything a lot harder.
Any other suggestions for solutions to the problem are also welcome.
Main.dart here:
import 'package:flutter/material.dart';
import 'home_widget.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_analytics/observer.dart';
void main() => runApp(App());
class App extends StatelessWidget {
static FirebaseAnalytics analytics = FirebaseAnalytics();
static FirebaseAnalyticsObserver observer =
FirebaseAnalyticsObserver(analytics: analytics);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My Flutter App',
navigatorObservers: <NavigatorObserver>[observer],
home: Home(
analytics: analytics, //
observer: observer, //
),
);
}
}
My home widget currently looks like this:
import 'package:flutter/material.dart';
import 'placeholder_widget.dart';
import 'homepage.dart';
import 'reader.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_analytics/observer.dart';
class Home extends StatefulWidget {
Home({Key key, this.title, this.analytics, this.observer}) //
: super(key: key); //
final String title; //
final FirebaseAnalytics analytics; //
final FirebaseAnalyticsObserver observer; //
#override
State<StatefulWidget> createState() {
return _HomeState(analytics, observer);
}
}
class _HomeState extends State<Home> {
_HomeState(this.analytics, this.observer); //
final FirebaseAnalyticsObserver observer; //
final FirebaseAnalytics analytics; //
int _currentIndex = 0;
final List<Widget> _children = [
Homepage(),
MyApp(),
PlaceholderWidget(Colors.green) // TODO: I want my chat button here
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: _children[_currentIndex], // new
bottomNavigationBar: BottomNavigationBar(
onTap: onTabTapped, // new
currentIndex: _currentIndex, // new
items: [
new BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.photo_camera),
title: Text('blah'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text('Chat')
)
],
),
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
}
Use this package flutter_tawk.
import 'package:flutter_tawk/flutter_tawk.dart';
Tawk(
directChatLink: 'YOUR_DIRECT_CHAT_LINK',
visitor: TawkVisitor(
name: 'Ayoub AMINE',
email: 'ayoubamine2a#gmail.com',
),
)
I solved it. The best way I found was using javascript inside of the webview, and then you can evaluate javascript in the webview through the webview controller.
I made Tawk.to default to hiding on my website, and to show it you simply do the following:
Declare a controller for the webview. At the start of your class add:
WebViewController _controller;
Then inside of your WebView() widget:
new WebView(
initialUrl: 'google.com',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) async {
_controller = webViewController;
//I've left out some of the code needed for a webview to work here, fyi
},
),
And finally you can make a button in your appbar run the javascript code to open tawk:
IconButton(
icon: Icon(Icons.message),
onPressed: () {
_controller.evaluateJavascript('Tawk_API.showWidget();');
_controller.evaluateJavascript('Tawk_API.maximize();');
},
),

How do i run the charts in main dart? - flutter

I am trying to run charts from a library in flutter. I can't run in using main.dart.
Main.dart:
import './widgets/chart.dart';
void main() {
runApp(
new MaterialApp(
home: PointsLineChart(),
),
);
}
chart.dart:
import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart' as charts;
class PointsLineChart extends StatelessWidget {
final List<charts.Series> seriesList;
final bool animate;
PointsLineChart(this.seriesList, {this.animate});
/// Creates a [LineChart] with sample data and no transition.
factory PointsLineChart.withSampleData() {
return new PointsLineChart(
_createSampleData(),
// Disable animations for image tests.
animate: false,
);
}
#override
Widget build(BuildContext context) {
return new charts.LineChart(seriesList,
animate: animate,
defaultRenderer: new charts.LineRendererConfig(includePoints: true));
}
/// Create one series with sample hard coded data.
static List<charts.Series<LinearSales, int>> _createSampleData() {
final data = [
new LinearSales(0, 5),
new LinearSales(1, 25),
new LinearSales(2, 100),
new LinearSales(3, 75),
];
return [
new charts.Series<LinearSales, int>(
id: 'Sales',
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
domainFn: (LinearSales sales, _) => sales.year,
measureFn: (LinearSales sales, _) => sales.sales,
data: data,
)
];
}
}
/// Sample linear data type.
class LinearSales {
final int year;
final int sales;
LinearSales(this.year, this.sales);
}
The error that i am getting is that in main.dart i need to input something in order to run chart.dart. In PointsLineChart(),
it gives me error:
1 required argument(s) expected, but 0 found.dart(not_enough_required_arguments)
(new) PointsLineChart(List> seriesList, {bool animate}) → PointsLineChart
The constructor of PointsLineChart takes two argument:
PointsLineChart(this.seriesList, {this.animate});
whereas in your main.dart, you give no arguments:
home: PointsLineChart(),// here you need to add two arguments!!!
Change the code as below:
runApp(
new MaterialApp(
home: PointsLineChart(PointsLineChart.createSampleData(), animate: false),
),
);
also change _createSampleData to createSampleData to make it public.

Categories

Resources