Error thrown on navigator push : “!_debugLocked': is not true.” - flutter

When I tried to push from the initstate , this error shows up. Help
Import files here
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:corona_app/screens/home.dart';
Loading Screen Stateful Widget
class LoadingScreen extends StatefulWidget {
#override
_LoadingScreenState createState() => _LoadingScreenState();
}
class _LoadingScreenState extends State<LoadingScreen> {
#override
initState
void initState() {
// TODO: implement initState
super.initState();
Navigator.push(context, MaterialPageRoute(builder: (context)=>Home()));
}
#override
Widget build
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SpinKitPouringHourglass(
color: Colors.green,
size: 100,
),
),
);
}
}

Alpha Bot Try this code:
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) => _afterLayout(context));
super.initState();
}
Then create this method and navigate to your Home:
_afterLayout(BuildContext context) {
// TODO
Navigator.push(context, MaterialPageRoute(builder: (context)=>Home()));
}

Related

Flutter splash screen error - Navigator operation requested with a context that does not include a Navigator. How can I solve this error

Edit: (main.dart)
Added Sentry which actually starts the app
Future<void> main() async {
await SentryFlutter.init(
(options) {
options.dsn = _sentryDSN;
// Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.
// We recommend adjusting this value in production.
options.tracesSampleRate = _sentryTracesSampleRate;
options.attachStacktrace = true;
options.enableAppLifecycleBreadcrumbs = true;
},
appRunner: () => runApp(const SplashScreen()),
);
// or define SENTRY_DSN via Dart environment variable (--dart-define)
}
New to flutter, creating a splash screen to an app that was built with MaterialApp but getting an error. HOw can I solve this without a onPress function
Error:
Exception has occurred.
FlutterError (Navigator operation requested with a context that does not include a Navigator.
The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.)
import 'package:flutter/material.dart';
import 'package:loopcycle/screens/loopcycle_main.dart';
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
#override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
_navigateToMainApp();
}
void _navigateToMainApp() async {
await Future.delayed(const Duration(milliseconds: 2000), () {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => const LoopcycleMainApp()));
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Builder(
builder: (context) => const Center(
child: Text("test"),
)),
);
}
}
Thank you in advance.
EDIT: I changed the solution after you provided more information about the code.
This error is happening because you are using a context that does not have a Navigator in it, this is happening probrably because the widget that you are getting the context is parent of the MaterialApp() widget, to solve it you should create another widget that is a child of the MaterialApp() instead of using the parent widget, let me give you an example instead:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: GestureDetector(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SomeWidget(),
),
),
child: Container(
height: 300,
width: 300,
color: Colors.red,
),
),
);
}
}
This may give an error because you are using the context of a widget that is the parent of the MaterialApp() widget, to solve it just create another widget that is a child of MaterialApp().
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: AnotherWidget(),
);
}
}
class AnotherWidget extends StatelessWidget {
const AnotherWidget({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SomeWidget(),
),
),
child: Container(
height: 300,
width: 300,
color: Colors.red,
),
),
);
}
}
I was playing with your code, and fixed it for you, and there are basically two ways to solve it, you can create a MaterialApp() before calling the SplashScreen() in the runApp() function like so:
import 'package:flutter/material.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:loopcycle/screens/loopcycle_main.dart';
Future<void> main() async {
await SentryFlutter.init(
(options) {
options.dsn = _sentryDSN;
// Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.
// We recommend adjusting this value in production.
options.tracesSampleRate = _sentryTracesSampleRate;
options.attachStacktrace = true;
options.enableAppLifecycleBreadcrumbs = true;
},
appRunner: () => runApp(
const MaterialApp(
home: SplashScreen(),
),
),
);
// or define SENTRY_DSN via Dart environment variable (--dart-define)
}
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
#override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
_navigateToMainApp();
}
void _navigateToMainApp() async {
await Future.delayed(const Duration(milliseconds: 2000), () {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => const LoopcycleMainApp()));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Builder(
builder: (context) => const Center(
child: Text("test"),
)),
);
}
}
Or you can create an intermediate widget to hold the MaterialApp() and then inside this widget you can call SplashScreen(), like so:
import 'package:flutter/material.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:loopcycle/screens/loopcycle_main.dart';
Future<void> main() async {
await SentryFlutter.init(
(options) {
options.dsn = _sentryDSN;
// Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.
// We recommend adjusting this value in production.
options.tracesSampleRate = _sentryTracesSampleRate;
options.attachStacktrace = true;
options.enableAppLifecycleBreadcrumbs = true;
},
appRunner: () => runApp(const MyApp()),
);
// or define SENTRY_DSN via Dart environment variable (--dart-define)
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: SplashScreen(),
);
}
}
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
#override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
_navigateToMainApp();
}
void _navigateToMainApp() async {
await Future.delayed(const Duration(milliseconds: 2000), () {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => const LoopcycleMainApp()));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Builder(
builder: (context) => const Center(
child: Text("test"),
)),
);
}
}
In this second solution, the intermediate widget is the MyApp() widget, and in my opinion, I consider this solution as being the best one for your problem, because if you ever wanted to load a different screen based on the different states, for example:
If a user is signed in you load a home page, and if a user is not signed in you load a sign up page.
Doing this, or anything similar is much easier when you have this intermediate widget that holds the MaterialApp(), and you can even create some logic to integrate the Splash Screen too, but I don't know what you are trying to achieve, so pick the solution you find the best for your problem.

Flutter Context Error in Navigator pushReplacement

I am creating a Splash Screen and I am getting Context error in Navigator push replacement
Following is the code for splash screen in main.dart file
import 'dart:async';
import 'package:number_trivia/pages/home.dart';
void main() {
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
home: splash_screen(),
));
}
class splash_screen extends StatefulWidget {
#override
_splash_screenState createState() => _splash_screenState();
}
class _splash_screenState extends State<splash_screen> {
#override
void initState() {
super.initState();
Timer(Duration(seconds: 3),
()=>Navigator.pushReplacement(context,
MaterialPageRoute(builder:
(context) =>home()
)
)
);
}
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: FlutterLogo(size: MediaQuery.of(context).size.height,),
);
}
}
The error says - The argument type 'JsObject' can't be assigned to the parameter type 'BuildContext'.
How do I correct it?
Any help will be much appreciated:)
When widget build completed, you can call Timer function.
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
Timer(Duration(seconds: 3), () {
Navigator.pushReplacement(context,
MaterialPageRoute(builder:
(context) =>home()
)
);
});
});
}
to that pushReplacement method, you passed a context which wasn't specified upper in the widget tree.
try wrapping the screen with a widget that has a build method so that it creates a BuildContext that you can use.
like this:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: splash_screen(context),
);
}
}
class splash_screen extends StatefulWidget {
BuildContext context;
splash_screen(this.context);
#override
_splash_screenState createState() => _splash_screenState();
}
class _splash_screenState extends State<splash_screen> {
#override
void initState() {
super.initState();
Timer(
Duration(seconds: 3),
() => Navigator.pushReplacement(
widget.context, MaterialPageRoute(builder: (context) => home())));
}
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: FlutterLogo(
size: MediaQuery.of(context).size.height,
),
);
}
}
does that help?

Builder widget for ChangeNotifier in Flutter

Background
A ValueNotifier has a ValueListenableBuilder widget.
A Stream has a StreamBuilder widget.
A Future has a FutureBuilder widget.
Question
What is the builder for ChangeNotifier?
What I tried
I tried using a ValueListenableBuilder with ChangeNotifier but ChangeNotifier doesn't implement ValueListenable.
I know I could use ChangeNotifierProvider from the Provider package, but I'd like to know if there is a solution that doesn't require a third-party package.
This is a supplemental answer demonstrating using an AnimatedBuilder to rebuild the UI on a change from a ChangeNotifier.
It's just the standard counter app.
counter_model.dart
import 'package:flutter/foundation.dart';
class CounterModel extends ChangeNotifier {
int _counter = 0;
int get count => _counter;
void increment() {
_counter++;
notifyListeners();
}
}
main.dart
import 'counter_model.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _counterModel = CounterModel();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
AnimatedBuilder(
animation: _counterModel,
builder: (context, child) {
return Text(
'${_counterModel.count}',
style: Theme.of(context).textTheme.headline4,
);
}
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _counterModel.increment,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
ChangeNotifier is a direct implementation of the Listenable Widget and for the Listenable, you can use AnimatedBuilder, which triggers rebuilds from a Listenable without passing back a specific value
Also, your class could extend from ChangeNotifier and add new capability to it and you can create a custom Builder widget base on these new functionalities
You can wirte a simple widget by yourself.
use setState as a listener for a ChangeNotifier.
class ChangeNotifierBuilder<T extends ChangeNotifier> extends StatefulWidget {
const ChangeNotifierBuilder({
Key? key,
required this.value,
required this.builder,
}) : super(key: key);
final T value;
final Widget Function(BuildContext context, T value) builder;
#override
_ChangeNotifierBuilderState<T> createState() =>
_ChangeNotifierBuilderState<T>();
}
class _ChangeNotifierBuilderState<T extends ChangeNotifier>
extends State<ChangeNotifierBuilder<T>> {
#override
void initState() {
widget.value.addListener(_listener);
super.initState();
}
#override
void didUpdateWidget(covariant ChangeNotifierBuilder<T> oldWidget) {
if (widget.value != oldWidget.value) {
_miggrate(widget.value, oldWidget.value, _listener);
}
super.didUpdateWidget(oldWidget);
}
#override
void dispose() {
widget.value.removeListener(_listener);
super.dispose();
}
void _miggrate(Listenable a, Listenable b, void Function() listener) {
a.removeListener(listener);
b.addListener(listener);
}
void _listener() {
setState(() {});
}
#override
Widget build(BuildContext context) {
return widget.builder(context, widget.value);
}
}
You can use consumer for Change and the build of your UI!
Try out these - https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple
The builder for ChangeNotifierProvider, ChangeNotifierProvider.value and other providers is a Consumer:
ChangeNotifierProvider(
create: (context) => CounterModel(),
child: Consumer<CounterModel>(
builder: (context, model, child) {
return Text('${model.count}');
}
),
),
As of today, AnimatedBuilder is renamed and update as ListenableBuilder
Hope documents will be updated soon but you can see related issue and examples here https://github.com/flutter/flutter/pull/116543/files

How to navigate to main page after lottie splash.json completed in flutter?

How to navigate the main page after the Lottie animation completed in a flutter when I run the code it shows the Lottie animation repeatedly.
You can copy paste run full code below
You can use AnimationController and in Lottie onLoaded call AnimationController forward().whenComplete then you can do Navigator.push
code snippet
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
AnimationController _controller;
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this);
}
...
Lottie.asset(
'assets/LottieLogo1.json',
controller: _controller,
onLoaded: (composition) {
_controller
..duration = composition.duration
..forward().whenComplete(() => Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondPage()),
));
},
),
To run working demo, you need this file https://github.com/xvrh/lottie-flutter/blob/master/example/assets/LottieLogo1.json
working demo
full code
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
AnimationController _controller;
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: [
Lottie.asset(
'assets/LottieLogo1.json',
controller: _controller,
onLoaded: (composition) {
_controller
..duration = composition.duration
..forward().whenComplete(() => Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondPage()),
));
},
),
],
),
);
}
}
class SecondPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(body: Center(child: Text("Second Page")));
}
}

Flutter: Update TextFormField text with ChangeNotifier

In a complex scenario I need to update the text of some TextFormFields when a notifyListeners() is sent by a Model extending ChangeNotifier.
The problem is that to change the text of a TextFormField you have to use the setter TextFormField.text which implies a rebuild, and so you can't use it into the build method. But to access the Provider of the model you need the context which is inside the build method.
MWE (obviously the button is in another Widget in the real project, and there are more TextFormFields)
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyModel extends ChangeNotifier {
void updateCounter() {
++_counter;
notifyListeners();
}
MyModel() {
_counter = 1;
}
int _counter;
String get counter => _counter.toString();
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => MyModel(),
child: MaterialApp(
title: 'Test',
home: MyHomePage(),
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _text1Ctl = TextEditingController();
var _text2Ctl = TextEditingController();
#override
void initState() {
super.initState();
final model = MyModel();
model.addListener(() {
_text1Ctl.text = model.counter;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
FlatButton(
onPressed: () {
Provider.of<MyModel>(context, listen: false).updateCounter();
},
child: Text('Press me'),
),
// 1st attempt
// Doesn't work because the listener isn't applied to the instance of the model provided by the provider.
TextFormField(controller: _text1Ctl),
// 2nd attempt
// Works but with `Another exception was thrown: setState() or markNeedsBuild() called during build.` because it changes text via controller (which implies a rebuild) during building.
Consumer<MyModel>(builder: (context, model, child) {
_text2Ctl.text = model.counter;
return TextFormField(controller: _text2Ctl);
})
]));
}
}
Your second example works without any errors when I run it:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyModel extends ChangeNotifier {
void updateCounter() {
++_counter;
notifyListeners();
}
MyModel() {
_counter = 1;
}
int _counter;
String get counter => _counter.toString();
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => MyModel(),
child: MaterialApp(
title: 'Test',
home: MyHomePage(),
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _text2Ctl = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
FlatButton(
onPressed: () {
Provider.of<MyModel>(context, listen: false).updateCounter();
},
child: Text('Press me'),
),
// 2nd attempt
// Works but with `Another exception was thrown: setState() or markNeedsBuild() called during build.` because it changes text via controller (which implies a rebuild) during building.
Consumer<MyModel>(builder: (context, model, child) {
_text2Ctl.text = model.counter;
return TextFormField(controller: _text2Ctl);
})
]));
}
}