I have two different .dart file.
sidebar file, there is a function in this class to something
class SideBar extends StatefulWidget {
#override
_SideBarState createState() => _SideBarState();
}
class _SideBarState extends State<SideBar>
with SingleTickerProviderStateMixin<SideBar> {
void onIconPressed() {
print('123');
}
}
homescreen file;
class HomeScreen extends StatelessWidget {
static String routeName = "/HomeScreen";
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
appBar: AppBar(
leading: Builder(
builder: (BuildContext context) {
return IconButton(
icon: const Icon(Icons.menu),
onPressed: (){ }, ///////////////////////where i try to implement function
I want the call this function in another file. What i tried import sidebar.dart file as sidebar. Then call function like sidebar.onIconPressed() But nothing work. I looked widget communication thing but couldnt get it. Is there any easy way to solve this problem.
Edit: The reason why my solution not work because I acces the void which has a setstate. Thats why I always get null message
class SideBar extends StatefulWidget {
#override
_SideBarState createState() => _SideBarState();
}
class _SideBarState extends State<SideBar>
with SingleTickerProviderStateMixin<SideBar> {
#override
Widget build(BuildContext context) {
return IconButton(
icon: const Icon(Icons.menu),
onPressed: (){
onIconPressed();
},
void onIconPressed() {
print('123');
}
}
class HomeScreen extends StatelessWidget {
static String routeName = "/HomeScreen";
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
appBar: AppBar(
leading: Builder(
builder: (BuildContext context) {
return SideBar(....
Create an object for your class then you would be able to call the function through the object.
//define the object inside HomeScreen class
final SideBar sidebar = SideBar();
then in onPressed call the function sidebar. onIconPressed();
class SideBar extends StatefulWidget {
#override
_SideBarState createState() => _SideBarState();
void onIconPressed() {
print('123');
}
}
class _SideBarState extends State<SideBar>
with SingleTickerProviderStateMixin<SideBar> {
#override
Widget build(BuildContext context) {
return Container();
}
}
class HomeScreen extends StatelessWidget {
static String routeName = "/HomeScreen";
SideBar s = new SideBar();
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
appBar: AppBar(leading: Builder(builder: (BuildContext context) {
return IconButton(
icon: const Icon(Icons.menu),
onPressed: () {
s.onIconPressed();
});
}))));
}
}
Related
If I have stacks of screens: A -> B -> C, whethen I press back button on the simulator or press back button on the Scaffold's back button on C, I always returned to A, not B. Why? And how to fix it? I searched on the
Here's how I push and pop. These code are simplified to only show the push/pop and screen build functions.
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: UserListPage()
);
}
}
class UserListPage extends StatefulWidget {
#override
_UserListPageState createState() => _UserListPageState();
}
class _UserListPageState extends State<UserListPage> {
...
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("User list")),
body: Builder(builder: (context) {
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SessionListPage(userId: users[index].id)
),
child: ...
})
}),
);
}
...
}
class SessionListPage extends StatefulWidget {
final int userId;
SessionListPage({ this.userId }) : super();
#override
_SessionListPageState createState() => _SessionListPageState();
}
class _SessionListPageState extends State<SessionListPage> {
...
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Session list")),
body: Builder(builder: (context) {
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DrawingPage(userId: widget.userId, sessionId: sessions[index].id)
),
child: ...
})
}),
);
}
...
}
class DrawingPage extends StatefulWidget {
final int userId;
final int sessionId;
DrawingPage({ this.userId, this.sessionId }) : super();
#override
_State createState() => _State();
}
class _State extends State<DrawingPage> {
#override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text("Enter the Number")),
body: Builder(builder: (context) {
InkWell(
onTap: () { Navigator.pop(context); } // here it returns straight to UserListPage instead of SessionListPage
child: ...
})
}
));
}
}
One thing that I noticed is that for the context that was supplied to push and pop, is from Builder instead of the context supplied from Widget build function. Does it have any impact?
Please remove super(); from all places.
Young, It's working fine. Please check below link
Code
Another newbe to Flutter here. I really thought this will be 2 min job but I actually get stack here. I need to pass 'Hello' data
from modal widget
class ToolModal extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text('tools'),
FlatButton(
child: Text('save'),
onPressed: () => Navigator.pop(context, 'Hello'),
),
],
);
}
}
through Bar widget
class Bar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BarItem(
name: 'Option1',
icon: Icons.category,
onPressed: () {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => ToolModal(),
);
},
);
}
}
to my home screen
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return Bar();
}
}
I know(I thought I know) how to pass data from screen to screen but modal and widget between really let me down. Please can anyone help me with this? I really apologise for this question but I can not find an answer to this
You are close. showModalBottomSheet returns a Future. You can capture the resulting "Hello" in the following way:
class Bar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BarItem(
name: 'Option1',
icon: Icons.category,
onPressed: () async {
String hello = await showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => ToolModal(),
);
},
);
}
}
You could use a callback function to pass the data back to your home screen:
class Bar extends StatelessWidget {
final Function(String) onHello;
const Bar({Key key, this.onHello}): super(key: key);
#override
Widget build(BuildContext context) {
return BarItem(
name: 'Option1',
icon: Icons.category,
onPressed: () async {
String hello = await showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => ToolModal(),
);
onHello(hello);
},
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return Bar(onHello: (String hello) {
print(hello);
});
}
}
The thing I'm trying to do is, to change the colour of a RawMaterialButton when the button is clicked. Read about StatefulWidget and it seemed like it should work, but for some reason it doesn't.
flutter: Another exception was thrown: setState() called in constructor: ButtonTest#1a93b(lifecycle state: created, no widget, not mounted)
ButtonTest class:
class ButtonState extends StatefulWidget {
#override
State createState() => ButtonTest();
}
class ButtonTest extends State<ButtonState> implements Cipher {
#override
String icon = '';
#override
String title = '';
bool enabled = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.title),
),
body: RawMaterialButton(
shape: CircleBorder(side: BorderSide(color: Colors.black)),
fillColor: enabled ? Colors.blue : Colors.red,
onPressed: () {
setState(() {
this.enabled = true;
});
},
padding: EdgeInsets.all(0)),
);
}
}
Cipher class:
abstract class Cipher {
String icon;
String title;
Widget build(BuildContext context);
}
getCiphers()
getCiphers() {
final List<Cipher> ciphers = new List();
ciphers.add(ButtonTest());
return ciphers;
}
Main class:
void main() => runApp(CipherTools());
class CipherTools extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'CipherTools',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CipherScreen(
ciphers: getCiphers(),
),
);
}
}
class CipherScreen extends StatelessWidget {
final List<Cipher> ciphers;
CipherScreen({Key key, #required this.ciphers}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Ciphers'),
),
body: ListView.builder(
itemCount: ciphers.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(ciphers[index].title),
// When a user taps on the ListTile, navigate to the DetailScreen.
// Notice that we're not only creating a DetailScreen, we're
// also passing the current todo through to it!
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(cipher: ciphers[index]),
),
);
},
);
},
),
);
}
}
class DetailScreen extends StatelessWidget {
// Declare a field that holds the Todo
final Cipher cipher;
// In the constructor, require a Todo
DetailScreen({Key key, #required this.cipher}) : super(key: key);
#override
Widget build(BuildContext context) {
return cipher.build(context);
}
}
What am I doing wrong here?
Wrap setState() like this.
if(this.mounted) {
setState(() {
this.enabled = true;
});
}
A couple of things:
ButtonState should be called ButtonTest because this is the
StatefulWidget
ButtonTest should be called ButtonTestState because this is the State.
Then in DetailScreen, in the build() method, you could return the StatefulWidget (ButtonTest), like this:
#override
Widget build(BuildContext context) {
return ButtonTest();
}
i try to edit the value of the child widget, i can do it with StatefulWidget parent but i want to do it with StatelessWidget parent and without using global value
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(child: Text('addFile'), onPressed: () {}),
FlatButton(child: Text('deleteFile'), onPressed: () {})
],
),
body: Child(),
);
}
}
class Child extends StatefulWidget {
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
var hasFile = true;
#override
Widget build(BuildContext context) {
return hasFile ? Text('has a file') : Text("no File");
}
}
You are thinking the wrong way. Child aka Text() should get its value from a model which is managed by the application or at least managed by the widget above. I would go with the provider package https://pub.dev/packages/provider and do this:
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
class MyState with ChangeNotifier {
String _myText;
MyState(this._myText);
getMyText() => _myText;
void changeText(String newText) {
_myText = newText;
notifyListeners();
}
}
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (_) => MyState("initial Text")),
],
child: Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(
child: Text('addFile'),
onPressed: () {
Provider.of<MyState>(context).changeText("addFile");
}),
FlatButton(
child: Text('deleteFile'),
onPressed: () {
Provider.of<MyState>(context).changeText("deleteFile");
})
],
),
body: Child(),
));
}
}
class Child extends StatelessWidget {
#override
Widget build(BuildContext context) {
MyState myState = Provider.of<MyState>(context);
return Text(myState.getMyText());
}
}
This is coded without IDE support or even compiling and running. But it should get you to the right direction.
You can use BLoC pattern to implement this kind of functionality,
Here is the BLoC class which will handle state of bool
import 'dart:async';
class Bloc {
final _fileController = StreamController<bool>();
changeState(bool val) {
_fileController.sink.add(val);
}
get hasFile => _fileController.stream;
dispose() {
_fileController.close();
}
}
final bloc = Bloc();
Then you can add stream builder in your Stateful Widget, in which you will provide stream of BLoC class.
StreamBuilder updates it's UI according to Stream.
class Child extends StatefulWidget {
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
var hasFile = true;
#override
void dispose() {
bloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: bloc.hasFile,
initialData: false,
builder: (context, snapshot) {
return snapshot.data ? Text('has a file') : Text("no File");
},
);
}
}
At last you can access BLoC class with your stateless widget as follows
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(
child: Text('addFile'),
onPressed: () {
bloc.changeState(true);
}),
FlatButton(
child: Text('deleteFile'),
onPressed: () {
bloc.changeState(false);
})
],
),
body: Child(),
);
}
}
Full example is as below
import 'package:flutter/material.dart';
import 'dart:async';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Homepage(),
);
}
}
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(
child: Text('addFile'),
onPressed: () {
bloc.changeState(true);
}),
FlatButton(
child: Text('deleteFile'),
onPressed: () {
bloc.changeState(false);
})
],
),
body: Child(),
);
}
}
class Child extends StatefulWidget {
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
var hasFile = true;
#override
void dispose() {
bloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: bloc.hasFile,
initialData: false,
builder: (context, snapshot) {
return snapshot.data ? Text('has a file') : Text("no File");
},
);
}
}
class Bloc {
final _fileController = StreamController<bool>();
changeState(bool val) {
_fileController.sink.add(val);
}
get hasFile => _fileController.stream;
dispose() {
_fileController.close();
}
}
final bloc = Bloc();
I have a scenario wherein I check the value of SharePreferences based on the value it will redirect the user to HomePage or LandingPage. I am not sure where did I got wrong? but I am getting this error below: I guess its not getting the context right any idea how do I get it?.
Unhandled Exception: Navigator operation requested with a context that does not include a Navigator.
E/flutter (11533): 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.
Here is my code:
import 'package:credit/src/pages/landing.dart';
import 'package:flutter/material.dart';
import 'package:credit/src/pages/credit/home.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
_LoadingPageState createState() => _LoadingPageState();
}
class _LoadingPageState extends State<MyApp> {
#override
void initState() {
super.initState();
getUserStatus().then((userStatus) {
if (userStatus == null) {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return LandingPage();
}));
} else {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return HomePage();
}));
}
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: CircularProgressIndicator(),
));
}
}
Future<String> getUserStatus() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String userStatus = prefs.getString('userstatus');
print("==On Load Check ==");
print(userStatus);
return userStatus;
}
When you call Navigator.of(context) framework goes up in widget tree attached to provided context and tries to find the closest Navigator.
The widget tree you showed does not have one, so you need to include Navigator in the widget tree.
Easiest option is to use MaterialApp with your widget passed as home. MaterialApp is creating navigator inside itself. (CupertinoApp does it too)
Updated code from original example:
import 'package:credit/src/pages/landing.dart';
import 'package:flutter/material.dart';
import 'package:credit/src/pages/credit/home.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: LoadingPage(),
);
}
}
class LoadingPage extends StatefulWidget {
LoadingPage({Key key}) : super(key: key);
_LoadingPageState createState() => _LoadingPageState();
}
class _LoadingPageState extends State<LoadingPage> { // note type update
#override
void initState() {
super.initState();
getUserStatus().then((userStatus) {
if (userStatus == null) {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return LandingPage();
}));
} else {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return HomePage();
}));
}
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: CircularProgressIndicator(),
));
}
}
Future<String> getUserStatus() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String userStatus = prefs.getString('userstatus');
print("==On Load Check ==");
print(userStatus);
return userStatus;
}
I have changed my code from
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo App',
theme: ThemeData(
primarySwatch: white,
scaffoldBackgroundColor: Colors.white,
),
home: Scaffold(
appBar: AppBar(
title: Text('Demo App'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
HomeScreen(title: 'Demo Home')));
},
child: Text('Open Home Screen'))
],
),
),
),
);
}
To
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo App',
theme: ThemeData(
primarySwatch: white,
scaffoldBackgroundColor: Colors.white,
),
home: InitScreen());
}
}
class InitScreen extends StatelessWidget {
const InitScreen({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Demo App'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
HomeScreen(title: 'Demo Home')));
},
child: Text('Open Home Screen'))
],
),
),
);
}
What changed?
Create a separate widget for home code in MyApp with InitScreen
What was the issue?
When we try to push Route by using Navigator.of(context), flutter will
try to find Navigator in the widget tree of the given context. In the
initial code, there was no widget that has Navigator. So, create a
separate widget for home code. And the MaterialApp widget in MyApp
will have Navigator.