I am trying to navigate to a new screen after the splash screen, when i try to navigate to new screen in the init method it shows an error,
E/flutter ( 5636): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: Navigator operation requested with a context that does not include a Navigator.
E/flutter ( 5636): 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.
My code
import 'package:flutter/material.dart';
import 'package:igloled_app/home.dart';
import 'dart:async';
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
Future.delayed(Duration(seconds: 3),(){
print('3 sec done');
Navigator.push(context, MaterialPageRoute(builder: (context){
return AppHeader();
}));
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Stack(
fit: StackFit.expand,
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Colors.white70,
),
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(flex: 2,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
margin: EdgeInsets.all(10.0),
child: Image.asset('images/splash_logo.png')
),
)
],
),
),
),
Expanded(flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(valueColor: AlwaysStoppedAnimation<Color>(Colors.green),),
Padding(padding: EdgeInsets.only(top: 20.0),
),
Text('Make Your Life Brighter',
style: TextStyle(
color: Colors.lightGreen,
fontStyle: FontStyle.italic,
fontSize: 20.0,
),
),
],
),
)
],
),
],
),
),
);
}
}
}
Can anyone please help me with this.
Thank you in advance.
Here is a sample code of doing it right way.
void main() => runApp(MaterialApp(home: FirstScreen()));
class FirstScreen extends StatefulWidget {
#override
_FirstScreenState createState() => _FirstScreenState();
}
class _FirstScreenState extends State<FirstScreen> {
#override
void initState() {
super.initState();
Future.delayed(Duration(seconds: 3), () {
print('3 sec done');
Navigator.push(context, MaterialPageRoute(builder: (context) {
return SecondScreen();
}));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text("First screen")));
}
}
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text("Second screen")));
}
}
your MaterialApp is not in your context !!
a slight fix to move the MaterialApp to the runApp method.
but for a notice please but all underground widgets that won't use the setState to another class or to the runApp method to have the best functionality of your code .
Related
I'm new to flutter and I have a problem with the navigation. My app has multiple pages and I can't understand the error and how to fix it.
I searched online and I tried everything that I found on Git / Stack or Google but nothing works.
This is the error : '!_debugLocked': is not true.
This is the code :
This is the first .dart file and the error is when I try to do the Navigator.of .....
import 'package:flutter/material.dart';
class FirstScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
SizedBox(
height: 320,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'C.R',
style: TextStyle(
fontSize: 120,
fontFamily: 'Membra',
),
)
],
),
SizedBox(
height: 200,
),
FloatingActionButton(
backgroundColor: Colors.white,
child: Image.asset('images/arrow_next.png'),
onPressed: () {
Navigator.of(context).pushNamedAndRemoveUntil(
'/SecondScreen', (Route<dynamic> route) => false);
},
),
],
),
);
}
}
This is the second .dart file where I want to go to from the first file/ page.
import 'package:flutter/material.dart';
import 'NewWidget_Creator.dart';
class SecondScreen extends StatefulWidget {
#override
_SecondScreenState createState() => _SecondScreenState();
}
class _SecondScreenState extends State<SecondScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Center(
child: NewWidget(
'Schede',
Navigator.of(context).pushNamedAndRemoveUntil(
'/SecondScreen', (Route<dynamic> route) => false)),
),
NewWidget(
'Orario',
Navigator.of(context).pushNamedAndRemoveUntil(
'/SecondScreen', (Route<dynamic> route) => false)),
NewWidget(
'Pausa',
Navigator.of(context).pushNamedAndRemoveUntil(
'/SecondScreen', (Route<dynamic> route) => false)),
],
),
);
}
}
NewWidgetCreator .dart file :
import 'package:flutter/material.dart';
class NewWidget extends StatelessWidget {
String Name_of_button;
Future<dynamic> send_toNextPage;
NewWidget(this.Name_of_button, this.send_toNextPage);
#override
Widget build(BuildContext context) {
return FlatButton(
onPressed: () {
send_toNextPage;
},
child: Container(
child: Text(
Name_of_button,
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Membra',
fontSize: 50,
),
),
),
);
}
}
Thank you in advance.
Why in NewWidget field send_toNextPage is a Future? Here you have fixed class of NewWidget
class NewWidget extends StatelessWidget {
String buttonName;
String nextPageRoute;
NewWidget(this.buttonName, this.nextPageRoute);
#override
Widget build(BuildContext context) {
return FlatButton(
onPressed: () {
Navigator.of(context).pushNamed(nextPageRoute);
},
child: Container(
child: Text(
buttonName,
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Membra',
fontSize: 50,
),
),
),
);
}
}
This is how to use it in SeconScreen:
class SecondScreen extends StatefulWidget {
#override
_SecondScreenState createState() => _SecondScreenState();
}
class _SecondScreenState extends State<SecondScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
NewWidget('Schede', '/SecondScreen'),
NewWidget('Orario', '/SecondScreen'),
NewWidget('Pausa', '/SecondScreen'),
],
),
);
}
}
BTW: SecondSreen can be StatelessWidget:
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
NewWidget('Schede', '/SecondScreen'),
NewWidget('Orario', '/SecondScreen'),
NewWidget('Pausa', '/SecondScreen'),
],
),
);
}
}
The following is a example code from :
https://bloclibrary.dev/#/flutterbloccoreconcepts
I am getting the following exception,
How can I fix the following Exception and why is this happening:
════════ Exception caught by widgets library
═══════════════════════════════════════════════════════
The following assertion was thrown building CounterPage(dirty):
BlocProvider.of() called with a context that does not contain a Cubit
of type CounterBloc.
No ancestor could be found starting from the context that was passed
to BlocProvider.of().
This can happen if the context you used comes from a widget above the
BlocProvider.
The context used was: CounterPage(dirty)
main.dart
import 'package:flutter/material.dart';
import 'counter_page.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Bloc Counter Example',
home: CounterPage(),
);
}
}
counter_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import'package:flutterbloc_counter_app/counter_bloc.dart';
class CounterPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
final CounterBloc counterBloc = BlocProvider.of<CounterBloc>(context);
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: BlocBuilder<CounterBloc, int>(
builder: (context, count) {
return Center(
child: Text(
'$count',
style: TextStyle(fontSize: 24.0),
),
);
},
),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
counterBloc.add(CounterEvent.increment);
},
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: FloatingActionButton(
child: Icon(Icons.remove),
onPressed: () {
counterBloc.add(CounterEvent.decrement);
},
),
),
],
),
);
}
}
counter_bloc.dart
import 'package:flutter_bloc/flutter_bloc.dart';
enum CounterEvent { increment, decrement }
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0);
#override
Stream<int> mapEventToState(CounterEvent event) async* {
switch (event) {
case CounterEvent.decrement:
yield state - 1;
break;
case CounterEvent.increment:
yield state + 1;
break;
}
}
}
You can not use 'BlocProvider.of' without BlocProvider at the parent.
You just create a 'CounterBloc' and pass the bloc to BlocBuilder.
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import'package:flutterbloc_counter_app/counter_bloc.dart';
class CounterPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
final CounterBloc counterBloc = CounterBloc();
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: BlocBuilder<CounterBloc, int>(
bloc: counterBloc,
builder: (context, count) {
return Center(
child: Text(
'$count',
style: TextStyle(fontSize: 24.0),
),
);
},
),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
counterBloc.add(CounterEvent.increment);
},
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: FloatingActionButton(
child: Icon(Icons.remove),
onPressed: () {
counterBloc.add(CounterEvent.decrement);
},
),
),
],
),
);
}
}
It looks like I forgot to provide an instance of the bloc to the CounterPage. Wrapping the CounterPage widget in a BlocProvider and create the instance of the bloc like this will do the trick:
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Bloc Counter Example',
home: BlocProvider(
create: (context) => CounterBloc(),
child: CounterPage(),
),
);
}
}
So, as it is in the title, I'm facing problem with preventing a group of elements from moving, when one of the elements, wrapped in Visibility, becomes visible/invisible. I am new to Flutter, so I couldn't neither solve the problem nor find the solution anywhere.
I created sample code and made 2 screenshots to show the problem more clearly.
screenshots:
State 1, before the button is clicked
State 2, after the button is clicked (button clicked changes visibility of the text below)
code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'MyApp',
home: MyAppHome()
);
}
}
class MyAppHome extends StatefulWidget {
#override
_MyAppHomeState createState() => _MyAppHomeState();
}
class _MyAppHomeState extends State<MyAppHome> {
bool isVisible = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: _buildStateWidget()
);
}
Widget _buildStateWidget() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text('Some text 1', style: TextStyle(fontSize: 30.0)),
SizedBox(
width: 200.0,
height: 200.0,
child: FloatingActionButton(
onPressed: () {
setState(() {
isVisible = !isVisible;
});
},
)),
Visibility(
visible: isVisible,
child: Column(
children: <Widget>[
Text(
'Some text 2',
textAlign: TextAlign.center,
),
Icon(
Icons.favorite_border
)
]
)
)
],
)
);
}
}
I understand, why it behaves like this, but I don't know how to fix that problem.
I think you could wrap your FAB and the widget below it with Expanded
Here's my solution:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(title: 'MyApp', home: MyAppHome());
}
}
class MyAppHome extends StatefulWidget {
#override
_MyAppHomeState createState() => _MyAppHomeState();
}
class _MyAppHomeState extends State<MyAppHome> {
bool isVisible = false;
#override
Widget build(BuildContext context) {
return Scaffold(body: _buildStateWidget());
}
Widget _buildStateWidget() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text('Some text 1', style: TextStyle(fontSize: 30.0)),
Expanded(
child: Container(
height: 200.0,
width: 200.0,
child: FittedBox(
child: FloatingActionButton(
onPressed: () {
setState(() {
isVisible = !isVisible;
});
},
),
),
),
),
Expanded(
child: Visibility(
visible: isVisible,
child: Column(children: <Widget>[
Text(
'Some text 2',
textAlign: TextAlign.center,
),
Icon(Icons.favorite_border)
])))
],
));
}
}
NOTE: I've changed the way you resize your FAB
I'm working through understanding how the Provider package works with Flutter but confused on how listen:false works.
I wrote some basic code using the usual Counter example from a new Flutter project. I created three types of stateless widgets each using a Provider:
Provider.of(context)
Consumer
Provider.of(context, listen: false)
The third example was to show how access the provider object and call methods on it without it rebuilding.
When I run the application all of the widget counts are changing - and I only expect it to change in the first two.
This is a simple example - what am I doing wrong?
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (_) => Counter()),
],
child: MaterialApp(
title: 'Provider Demo',
theme: ThemeData(
primarySwatch: Colors.amber,
),
home: MyHomePage(title: 'Provider Demo Home Page'),
),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
Widget build(BuildContext context) {
Counter counter = Provider.of<Counter>(context);
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ExampleProviderWidget(),
ExampleConsumerWidget(),
ExampleNoListenWidget()
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => counter.increment(),
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class ExampleProviderWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
Counter counter = Provider.of<Counter>(context);
return Container(
color: Colors.green,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Provider.of<Counter>(context):',
),
Text(
'${counter.count}',
style: Theme.of(context).textTheme.display1,
),
],
),
),
);
}
}
class ExampleConsumerWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<Counter>(
builder: (context, counter, _) {
return Container(
color: Colors.blue,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Consumer<Counter>(context):',
),
Text(
'${counter.count}',
style: Theme.of(context).textTheme.display1,
),
],
),
),
);
},
);
}
}
class ExampleNoListenWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
Counter counter = Provider.of<Counter>(context, listen: false);
return Container(
color: Colors.red,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Provider.of<Counter>(context, listen: false):',
),
Text(
'${counter.count}',
style: Theme.of(context).textTheme.display1,
),
RaisedButton(
child: Text("Increment"),
onPressed: () => counter.increment(),
)
],
),
),
);
}
}
That's because, while the widget that called Provider.of with listen:false did not want to rebuild, its parent forced it to.
In your example, when Counter changes, MyHomePage rebuilds and recreate the widget that specified listen:false, which in turn force it to rebuild too.
MyHomePage should specify listen: false too here.
What Remi suggested is a working approach (hack).
But I believe the better way in this case will be to remove the call
Counter counter = Provider.of(context); from the MyHomePageClass. That call is polluting the scope of your Counter object.
As suggested by flutter.io , It is best practice to put your Consumer widgets as deep in the tree as possible. As you did in the ExampleProviderWidget(), ExampleConsumerWidget(),ExampleNoListenWidget(). So make the floatingActionButton a separate widget class and have its own Provider.of(context).
And your MyHomePageClass will not have to call Provider.of.
listen: false necessary to be able to call Provider.of inside [State.initState] or the create method of providers.
listen: true, latter value changes will trigger a new [State.build] to widgets, and [State.didChangeDependencies] for [StatefulWidget].
According to the link below
https://docs.flutter.dev/development/data-and-backend/state-mgmt/simple#providerof
when listen is set false, it won’t cause this widget to rebuild when notifyListeners is called.
It works for me
I/flutter (12384): MyApp build
I/flutter (12384): MyHomePage build
I/flutter (12384): ExampleProviderWidget build
I/flutter (12384): ExampleConsumerWidget build
I/flutter (12384): ExampleNoListenWidget build
Reloaded 1 of 524 libraries in 792ms.
I/flutter (12384): ExampleProviderWidget build
I/flutter (12384): ExampleProviderWidget build
I/flutter (12384): ExampleProviderWidget build
I/flutter (12384): ExampleProviderWidget build
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
print('MyApp build');
return MaterialApp(
title: 'Provider Demo',
theme: ThemeData(
primarySwatch: Colors.amber,
),
home: MyHomePage(title: 'Provider Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
Counter _counter = Counter();
#override
Widget build(BuildContext context) {
print('MyHomePage build');
return ChangeNotifierProvider.value(
value: _counter,
child: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ExampleProviderWidget(),
ExampleConsumerWidget(),
ExampleNoListenWidget()
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => _counter.increment(),
tooltip: 'Increment',
child: Icon(Icons.add),
),
),
);
}
}
class ExampleProviderWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
print('ExampleProviderWidget build');
Counter counter = Provider.of<Counter>(context);
return Container(
color: Colors.green,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Provider.of<Counter>(context):',
),
Text(
'${counter.count}',
style: Theme.of(context).textTheme.display1,
),
],
),
),
);
}
}
class ExampleConsumerWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
print('ExampleConsumerWidget build');
return Consumer<Counter>(
builder: (context, counter, _) {
return Container(
color: Colors.blue,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Consumer<Counter>(context):',
),
Text(
'${counter.count}',
style: Theme.of(context).textTheme.display1,
),
],
),
),
);
},
);
}
}
class ExampleNoListenWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
print('ExampleNoListenWidget build');
Counter counter = Provider.of<Counter>(context, listen: false);
return Container(
color: Colors.red,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Provider.of<Counter>(context, listen: false):',
),
Text(
'${counter.count}',
style: Theme.of(context).textTheme.display1,
),
RaisedButton(
child: Text("Increment"),
onPressed: () => counter.increment(),
)
],
),
),
);
}
}
Whenever you say Provider.of<Counter>(context,, you should write , listen: false).
Provider.of<Counter>(context, listen: false);
from the code you have written, the increment method you are using in the onPressed method will not work, if you implement listen: false.
was testing AutomaticKeepAliveClientMixin and run into an issue,
page loses state after navigator.push
anyone knows this issue? any workarounds? be glad for any info, cheers
my goal is to keep the page state
steps to reproduce: open app click PageOne's push-button then go back swipe right and left and the page loses state
image
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: MyApp()));
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
initialIndex: 0,
length: 2,
child: Scaffold(
body: TabBarView(
children: <Widget>[Page1(), Page2()],
),
bottomNavigationBar: Material(
child: TabBar(
labelColor: Colors.black,
tabs: <Widget>[
Tab(
icon: Icon(Icons.check),
),
Tab(
icon: Icon(Icons.check),
),
],
),
),
),
),
);
}
}
class Page1 extends StatefulWidget {
#override
Page1State createState() {
return new Page1State();
}
}
class Page1State extends State<Page1> with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
Container(
height: 300,
color: Colors.orange,
),
Container(
height: 300,
color: Colors.pink,
),
Container(
height: 300,
color: Colors.yellow,
child: Center(
child: Container(height: 26,
child: MaterialButton(
color: Colors.blue,
child:
Text('clicking this and back then swipe => page loses state'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PushedPage()),
);
}),
),
),
),
],
);
}
#override
bool get wantKeepAlive => true;
}
class Page2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(height: 300, color: Colors.orange);
}
}
class PushedPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
color: Colors.blue,
),
);
}
}
From the documentation on AutomaticKeepAliveClientMixin:
A mixin with convenience methods for clients of
[AutomaticKeepAlive]. Used with [State] subclasses.
Subclasses must implement [wantKeepAlive], and their [build]
methods must call super.build (the return value will always return
null, and should be ignored).
So in your code, before you return the ListView just call super.build:
Widget build(BuildContext context) {
super.build(context);
return ListView(...
}