Hot reload is not working in a simple Hello World example. When trying to change the text, a string, to something else under run-time and hot reload it, nothing happens. I'm debugging on a physical device and I'm using VSCode.
import 'package:flutter/material.dart';
void main() {
String text = "Hello world";
runApp(Center(child: new Text(text, textDirection: TextDirection.ltr)));
}
Is hot reload not reliable or am i doing something wrong here?
EDIT: Found out that restarting the app, CTRL+SHIFT+F5, worked as the hot reload should.
Specifically, a hot reload causes all the existing widgets to rebuild. Only code involved in the rebuilding of the widgets are automatically re-executed.
https://flutter.dev/docs/development/tools/hot-reload
This means that you need a widget class which implements the build method to ensure the code is going to be re-executed on a hot reload.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
String text = "Hello world";
return Center(
child: new Text(text, textDirection: TextDirection.ltr),
);
}
}
If you create your class like this i think there are no problems :
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
String text="Hello World"
return MaterialApp(
home: Scaffold(
body: Center(
child: Text(text,textDirection: TextDirection.ltr),
)));
}
}
A few types of code changes cannot be hot reloaded though:
Global variable initializers
Static field initializers
The main() method of the app
For these changes you can fully restart your application, without having to end your > debugging session:
Don’t click the Stop button; simply re-click the Run button (if in a run session) or Debug button (if in a debug session), or shift-click the ‘hot reload’ button.
Found this on the Flutter developer portal.
Related
A flutter newbie, I'm trying to get my app to take values from a local json file and render them on-screen.
The logic isn't waiting for the class constructor to populate the relevant string variable before rendering the app on screen.
Here's the code that illustrates my problem.
First, my main.dart file:
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'sampleDataClass.dart';
import 'package:provider/provider.dart';
String assetFilePath = 'assets/basicData.json';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
FlutterError.onError = (details) {
FlutterError.presentError(details);
if (kReleaseMode) exit(1);
};
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => MyAppState(),
child: const MaterialApp(
title: "Sample screen",
home: MyHomePage(),
)
);
}
}
class MyAppState extends ChangeNotifier {
SampleDataClass current = SampleDataClass(assetFilePath);
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
#override
Widget build(BuildContext context) {
var myAppState = context.watch<MyAppState>();
var myAppBasicData = myAppState.current;
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.indigo,
foregroundColor: Colors.amberAccent,
title: const Text("This is the App Bar"),
elevation: 10,
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(children: [
Expanded(
child: Container(
color: Colors.blueGrey,
padding: const EdgeInsets.all(10),
child: (
Text(myAppBasicData.language,
style: const TextStyle(
color: Colors.white,
))))),
]),
]
),
);
}
}
Here is my SampleDataClass.dart file:
import 'dart:core';
import 'dart:convert';
import 'package:flutter/services.dart' show rootBundle;
class SampleDataClass {
String classFilePath = "";
String language = "not populated";
String batteryName = "not populated";
SampleDataClass(filePath) {
rootBundle.loadString(filePath).then((jsonDataString) {
Map classDataMap = jsonDecode(jsonDataString);
language = classDataMap['language'];
print(language);
batteryName = classDataMap['batteryName'];
print(batteryName);
});
}
Map<String, dynamic> toJson() => {
'language': language,
'batteryName': batteryName,
};
}
And here's my pubspec.yaml file:
name: samples
description: A new Flutter project.
environment:
sdk: '>=2.18.6 <3.0.0'
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
provider: ^4.1.1
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter:
uses-material-design: true
assets:
- assets/basicData.json
Finally, my basicData.json file looks like this:
{
"language": "English",
"batteryName": "Exercise 32"
}
The print statements in the sampleDataClass class work fine but as you can see if you run the code (from the command line using "flutter run --no-sound-null-safety"), the app renders the initial values of the variables before the class constructor assigns the json values to them.
What I expected was the class constructor to complete writing the relevant json values to the class members before handing back the class instance to the build widget in main.dart to render the app on-screen.
I realise that this is a very elementary question, but it is a key pending learning task for me!
Thank you all very much in advance!
it looks like rootBundle is an async function, you have to wait till it finishes then you notify the UI to render with new data,
How to handle this :
you should have 3 states loading state , error state , loaded state ,
create an init(path) function in SampleDataClass class that returns SampleDataClass instance.
Also, in the init() function at the beginning you have to change the state to loading state then when you get the data you set the state to loaded state, then notify listeners this will help the screen to know which state the page is in.
in the screen , call didChangeDependencies() and inside it, call your Provider => init function ( current = contecxt.read<YPUR_PROVIDER>().init(path); )
therefore the current object is being initialized, the page is showing a loader, and once the initialization is done, the provider will notify the page and it will change the loading view to the loaded view (your current view).
Another Tip::
your app will close whenever there is an error, it is not a good practice, since flutter will through non-fatal exception
FlutterError.onError = (details) {
FlutterError.presentError(details);
if (kReleaseMode) exit(1);
There is a choice of two excellent solutions to my problem with very little effort and no need for any change management (yet).
Indeed, so similar is the problem described that I probably should have found it before I posted this query here. As a newbie in this discipline, I clearly didn't conduct my search with the appropriate key words; for that I apologise!
Thanks very much to everyone for your patience.
The two solution options (which even I was able to follow) can be found here:
Calling an async method from a constructor in Dart
I currently have many problems in my app with the get package for Flutter (https://pub.dev/packages/get) and the following state scenario:
For example I have a GetxController UserController. I need this controller in different Widgets, so I initialize it with Get.put() in the first widget and for the other child widgets I'll call it with Get.find(). That works.
But: I have some widgets that sometimes load before the controller got initialized and sometimes after. So I get many "UsersController" not found errors. Maybe there exists some workaround for this problem?
You could initialize your UsersController class using a Bindings class, prior to the start of your app.
Example
class UsersController extends GetxController {
static UsersController get i => Get.find();
int userId = 5;
}
class UsersBinding extends Bindings {
#override
void dependencies() {
Get.put<UsersController>(UsersController());
}
}
void main() async {
UsersBinding().dependencies();
runApp(MyApp());
}
Done this way your UsersController is guaranteed to be available everywhere in your app, prior to any Widgets loading.
class MyHomePage extends StatelessWidget {
final UsersController uc = Get.find();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('GetX Bindings'),
),
body: Center(
child: Obx(() => Text('userId: ${uc.userId}')),
),
);
}
}
try to add
GetMaterialApp(
smartManagement: SmartManagement.keepFactory,
)
so that it can store factory of those instanse
or make sure add permanent
Get.put<Repo>(Repo(), permanent: true);
so that it never get deleted from memory
if u need to check class is initialized, u most keep the class in memory with permanent:trueand set tag:'anything you want for check this object' property in Get.put function, and now u can check
bool Get.isRegistered<className>(tag: 'TagInPut')
If there's no specific reason, you can just call Get.put(UserController()) at main function and wrap material app with GetMaterialApp.
if you are using get to provide an instance of something across your app
meaby https://pub.dev/packages/get_it could help you more. just be sure that you use allowReassignment==true so you can update your instance if you want save a full new controller, but if you want to use the same always get_it is perfect for you
When removing system overlays with SystemChrome.setEnabledSystemUIOverlays([]) the app does not expand to fill the screen.
Complete main.dart file:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setEnabledSystemUIOverlays([]);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Container(
color: Colors.red,
),
);
}
}
Without SystemChrome.setEnabledSystemUIOverlays([]):
Flutter version: 1.22.2
I'm unable to reproduce with Flutter 1.22(stable) but in https://github.com/flutter/flutter/issues/14432 people seem to have had good results with
setting Scaffold.resizeToAvoidBottomPadding to true.
You can also try to update Flutter and/or changing channels. Perhaps even trying on a physical device.
Where do I run initialisation code when starting a flutter app?
void main() {
return runApp(MaterialApp(
title: "My Flutter App",
theme: new ThemeData(
primaryColor: globals.AFI_COLOUR_PINK,
backgroundColor: Colors.white),
home: RouteSplash(),
));
}
If I want to run some initialisation code to, say fetch shared preferences, or (in my case) initialise a package (and I need to pass in the the BuildContext of the MaterialApp widget), what is the correct way to do this?
Should I wrap the MaterialApp in a FutureBuilder? Or is there a more 'correct' way?
------- EDIT ---------------------------------------------------
I have now placed the initialisation code in RouteSplash() widget. But since I required the BuildContext of the app root for the initialisation, I called the initialisation in the Widget build override and passed in context.ancestorInheritedElementForWidgetOfExactType(MaterialApp). As I don't need to wait for initialisation to complete before showing the splash screen, I haven't used a Future
One simple way of doing this will be calling the RouteSplash as your splash screen and inside it perform the initialization code as shown.
class RouteSplash extends StatefulWidget {
#override
_RouteSplashState createState() => _RouteSplashState();
}
class _RouteSplashState extends State<RouteSplash> {
bool shouldProceed = false;
_fetchPrefs() async {
await Future.delayed(Duration(seconds: 1));// dummy code showing the wait period while getting the preferences
setState(() {
shouldProceed = true;//got the prefs; set to some value if needed
});
}
#override
void initState() {
super.initState();
_fetchPrefs();//running initialisation code; getting prefs etc.
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: shouldProceed
? RaisedButton(
onPressed: () {
//move to next screen and pass the prefs if you want
},
child: Text("Continue"),
)
: CircularProgressIndicator(),//show splash screen here instead of progress indicator
),
);
}
}
and inside the main()
void main() {
runApp(MaterialApp(
home: RouteSplash(),
));
}
Note: It is just one way of doing it. You could use a FutureBuilder if you want.
To run code at startup, put it in your main.dart. Personally, that's the way I do it, to initialise lists etc.
I have just started using Flutter and i'm having this problem while running my code
"Another exception was thrown: type 'MyApp' is not a subtype of type 'StatelessWidget'".
And the interesting part is that i dont even have this 'StatelessWidget' in my code.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
List<String> _bars = ['Olivio bar'];
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Drinkzz'),
),
body: Column(
children: [
Container(
margin: EdgeInsets.all(10.0),
child: RaisedButton(
onPressed: () {
_bars.add('Riviera Bar');
},
child: Text('Add new Bar!'),
),
),
Column(
children: _bars
.map((element) => Card(
child: Column(
children: <Widget>[
Image.asset('assets/olivio.jpg'),
Text(element)
],
),
))
.toList(),
)
],
)),
);
}
}
I am really lost and would aprecciate some help!
Thanks,
As Jonah Williams said,
If you changed MyApp from a StatelessWidget to a StatefulWidget you
need to hot restart, since it is invoked in main
This has been explained multiple times in live coding sessions, that when you make changes in functions like initState(), you have to restart the app. A similar case applies for you, when you changed state related properties of the MyApp widget you need to restart your app for those changes to take effect.
Basically, when you hot reload the app, it calls the build() function, initState() is called only when you restart the app, so that the app reinitiates everything including the widget whose initState() function you changed.
you need to restart your app for the changes to take effect. Hot reload won't work at this time
You need to Hot Reload by using R (shift + r) coz you change MyApp class from StatelessWidget to a StatefulWidget while your app is running.
In the flutter If you need to change MyApp then you can not get result when after the app reloaded .You must need to restart you app and then you can check your edits are available on the app.
Since StatelessWidget was used at the begining of the code and was latter changed to a StatefulWidget you need to hot restart. It will continue to give error if you don't restart it because initially it's invoked in main
Quick Fix for Windows: To fix this, you need to stop main.dart by pressing Ctrl+F2 and run it again by pressing Shift+F10.
Basically, you will need to restart your app.
There is a simple way to solve this problem
Create a class above the "MyApp" class and call it any name you like, for instance "RealApp" but which is stateless and put "MyApp" stateful widget into it like so:
class RealApp extends StatelessWidget{
Widget build(Buildcontexr context){
#override
return MyApp();
}
The point is that you cannot run a Stateful widget in the runApp function but to get around this problem put the stateful widget into a stateless widget and your problem is solved.