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);
});
}
}
Related
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.
I was trying to pass argument to a button widget but I'm getting below error message.
Here is my argument:
ElevatedRegisterButton(
navigator: Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return const RegisterPage1();
})))
Here is my widget where I was trying to pass argument:
import 'package:flutter/material.dart';
class ElevatedRegisterButton extends StatefulWidget {
const ElevatedRegisterButton({super.key, required this.navigator});
final String navigator;
#override
State<ElevatedRegisterButton> createState() => _ElevatedRegisterButtonState();
}
class _ElevatedRegisterButtonState extends State<ElevatedRegisterButton> {
#override
Widget build(BuildContext context) {
return ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
onPressed: () {
widget.navigator;
},
child: const Text('Register'),
);
}
}
Here is the error message I'm getting:
The argument type 'Future' can't be assigned to the parameter type 'String'.
You need to set the navigator member to type final void Function(), because the onPressed property of ElevatedButton requires this type. You also need to pass it differently, because push is another type of function. Lastly, simply set onPressed to widget.navigator.
An example code is below based on your code snippet:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) =>
const MaterialApp(home: Scaffold(body: HomePage()));
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
#override
Widget build(BuildContext context) => SafeArea(
// see the difference, push is within () {}
child: ElevatedRegisterButton(navigator: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const RegisterPage1()));
}),
);
}
class ElevatedRegisterButton extends StatefulWidget {
const ElevatedRegisterButton({super.key, required this.navigator});
final void Function() navigator;
#override
State<ElevatedRegisterButton> createState() => _ElevatedRegisterButtonState();
}
class _ElevatedRegisterButtonState extends State<ElevatedRegisterButton> {
#override
Widget build(BuildContext context) => ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
// here simply set the function
onPressed: widget.navigator,
child: const Text('Register'),
);
}
// added for demo purposes
class RegisterPage1 extends StatelessWidget {
const RegisterPage1({super.key});
#override
Widget build(BuildContext context) =>
const Scaffold(body: SafeArea(child: Text('RegisterPage1')));
}
Problem 1
In your code:
class ElevatedRegisterButton extends StatefulWidget {
const ElevatedRegisterButton({super.key, required this.navigator});
// this line
final String navigator;
you are accepting a String navigator; but you are passing to it a Future:
ElevatedRegisterButton(
navigator: Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return const RegisterPage1();
})))
problem 2
If you try running your code, you'll get an error:
setState() or markNeedsBuild called during build
So, to fix the issues, refactor your code as follows:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light().copyWith(),
debugShowCheckedModeBanner: false,
home: SafeArea(
child: Scaffold(
body: ElevatedRegisterButton(navigator: Navigator.of(context))),
),
);
}
}
class ElevatedRegisterButton extends StatefulWidget {
const ElevatedRegisterButton({super.key, required this.navigator});
final NavigatorState navigator;
#override
State<ElevatedRegisterButton> createState() => _ElevatedRegisterButtonState();
}
class _ElevatedRegisterButtonState extends State<ElevatedRegisterButton> {
#override
Widget build(BuildContext context) {
return ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
onPressed: () {
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.navigator.push(MaterialPageRoute(
builder: (context) => Text("h1"),
));
});
},
child: const Text('Register'),
);
}
}
See also
setState() or markNeedsBuild called during build
I have List String that i want to pass to another screen using push in flutter.
List<String> example;
example : [
'a',
'b',
'c',
]
And i want to pass it to another List String named lagu in another screen using push.
I'm already trying using
Navigator.push(context, MaterialPageRoute(builder: (context){
return ScreenB(lagu: example);))}
But it give me one string which is everything inside example as single string when i check inside of lagu using print(lagu)
When i check print(lagu[0]) it give me '[' in console
any solution?
Try as follows:
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomePage(lagu:example)),
);
HomePage class
class HomePage extends StatefulWidget {
final List<String>? lagu;
const HomePage({this.lagu});
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<HomePage> {
#override
void initState() {
super.initState();
print(widget.lagu);
}
#override
Widget build(BuildContext context) {
return Scaffold(body: Text(widget.lagu![0].toString()));
}
}
you can solve the problem by passing the data using argument
you can pass the data like:
MaterialPageRoute(
builder: (context) => const ScreenB(),
settings: RouteSettings(
arguments: example,
),
and in your widget that is receiving the data (ScreenB) you can read the data like:
List recievedExample = ModalRoute.of(context)!.settings.arguments as List;
I have created working solution, everything works fine so you can adjust it.
class ScreenA extends StatefulWidget {
const ScreenA({Key? key}) : super(key: key);
#override
State<ScreenA> createState() => _ScreenAState();
}
class _ScreenAState extends State<ScreenA> {
List<String> example = [
'a',
'b',
'c',
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ElevatedButton(
child: Text('ScreenB'),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (ctx) => ScreenB(lagu: example)));
},
),
),
);
}
}
class ScreenB extends StatefulWidget {
final List<String> lagu;
ScreenB({Key? key, required this.lagu}) : super(key: key);
#override
State<ScreenB> createState() => _ScreenBState();
}
class _ScreenBState extends State<ScreenB> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: widget.lagu.map((e) => Text(e)).toList(),
),
),
);
}
}
I'm using Provider package to expose a simple boolean variabile that allow to change the status of variable "inAsyncCall" of the ModalProgressHUD widget.
When i try to do somethings before navigate to another page, and i want to display the circlular progress indicator during that computation, when the Future terminated, the current widget has been disposed and i cannot use Navigator.push():
Unhandled Exception: This widget has been unmounted, so the State no longer has a context (and should be considered defunct).
Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.
this is my Provider with ChangeNotifier class:
class CartProvider with ChangeNotifier {
bool _inAsync = false;
bool get inAsync => _inAsync;
void setInAsync(bool flag) {
this._inAsync = flag;
notifyListeners();
}
}
I inject the provider before the MaterialApp widget like this:
void main() async {
runApp(App());
}
class App extends StatefulWidget {
#override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => CartProvider(),
)
],
child: MaterialApp(
home: HomePage(),
),
);
}
}
And this is the simple home page where i access via context the provider injected:
class HomePage extends StatefulWidget {
HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
final cartProvider = context.watch<CartProvider>();
return ModalProgressHUD(
inAsyncCall: cartProvider.inAsync,
child: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('HOME'),
FirstStatefulWidget(),
],
),
),
bottomNavigationBar: SecondStatefulWidget(),
),
);
}
}
class FirstStatefulWidget extends StatefulWidget {
FirstStatefulWidget({Key? key}) : super(key: key);
#override
_FirstStatefulWidgetState createState() => _FirstStatefulWidgetState();
}
class _FirstStatefulWidgetState extends State<FirstStatefulWidget> {
late CartProvider cartProvider = context.read<CartProvider>();
Future doSomething() async {
cartProvider.setInAsync(true);
await Future.delayed(
Duration(seconds: 2),
() => {
cartProvider.setInAsync(false),
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondPage(),
),
)
},
);
}
#override
Widget build(BuildContext context) {
return Container(
child: ElevatedButton(
child: Text('do call'),
onPressed: doSomething,
),
);
}
}
class SecondStatefulWidget extends StatefulWidget {
SecondStatefulWidget({Key? key}) : super(key: key);
#override
_SecondStatefulWidgetState createState() => _SecondStatefulWidgetState();
}
class _SecondStatefulWidgetState extends State<SecondStatefulWidget> {
late CartProvider cartProvider = context.read<CartProvider>();
void goToAnotherPageAfterCall() async {
try {
cartProvider.setInAsync(true);
Future.delayed(
Duration(seconds: 2),
() => {
cartProvider.setInAsync(false),
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => SecondPage(),
),
)
},
);
} on Exception catch (e) {
cartProvider.setInAsync(false);
}
}
#override
Widget build(BuildContext context) {
return Container(
child: ElevatedButton(
child: Text('goToAnotherPage'),
onPressed: goToAnotherPageAfterCall,
),
);
}
}
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();
});
}))));
}
}