how to send data through different classes in different screens in flutter - dart

i was struck here while making an application my code went like this
void main() {
runApp(Myapp());
}
class Myapp extends StatelessWidget {
bool s=false;
#override
Widget build(BuildContext context) {
return (MaterialApp(
debugShowCheckedModeBanner: false,
title: "haha app",
theme: ThemeData(primarySwatch: Colors.lime),
home: s ? HomeScreen(null) : LoginPage()));
}
}
the above code is of main.dart file
and this is my another file called Login.dart and the code goes like this
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
Widget build(BuildContext context) {
return(some button ontap:(\\ on tap on this i have to change the bool s value in main.dart to true how to do that){
}
)
}
on tap the button the value s in main dart file should change to true but without navigator because we are not navigating here just a click.
please help me,
thanks in advance

You can use callbacks to communicate your widgets, like this
Create a method to get the callback , in this case : onChangeBool , pass the callback to your LoginPage Widget.
class Myapp extends StatelessWidget {
bool s=false;
onChangeBool(){
//change your var here
s = true;
//refresh the state
setState(() {
});
}
#override
Widget build(BuildContext context) {
return (MaterialApp(
debugShowCheckedModeBanner: false,
title: "haha app",
theme: ThemeData(primarySwatch: Colors.lime),
home: s ? HomeScreen(null) : LoginPage(onPressed: () => onChangeBool() ));
}
}
Receive the callBack , and call it when you press the button
class LoginPage extends StatefulWidget {
final VoidCallback onPressed;
LoginPage({this.onPressed});
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
Widget build(BuildContext context) {
return RaisedButton(
child: Text("button"),
onPressed: (){
widget.onPressed();
},
)
}
)
}
In case you want to pass Data, you can use ValueChanged callback , or if you want to pass complex data, create your own callback using typedef/
A sample using ValueChanged.
class Myapp extends StatelessWidget {
bool s=false;
receiveData(String data){
print("your text here : $data");
}
#override
Widget build(BuildContext context) {
return (MaterialApp(
debugShowCheckedModeBanner: false,
title: "haha app",
theme: ThemeData(primarySwatch: Colors.lime),
home: s ? HomeScreen(null) : LoginPage(onPressed: receiveData ));
}
}
class LoginPage extends StatefulWidget {
final ValueChanged<String> onPressed;
LoginPage({this.onPressed});
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
Widget build(BuildContext context) {
return RaisedButton(
child: Text("button"),
onPressed: (){
widget.onPressed("passing this data");
},
)
}
)
}

Related

Flutter simple implementation to handle click event with Bloc work once

I just learn about how can i use Bloc in flutter applications and my simple app i have some separated view class as App and MainPage and i implemented simple Bloc pattern to handle click event on some widgets such as button
after running application my implemented bloc pattern only work once and show message in console and after click again that don't work to show message
my main.dart class
void main() {
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
title: Strings.appName,
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: App(),
));
}
class App extends StatefulWidget {
final HomeBloc homeBloc = HomeBloc();
#override
State<App> createState() => MainPage();
}
MainPage class:
class MainPage extends State<App> {
#override
void initState() {
super.initState();
}
HomeBloc get _homeBloc => widget.homeBloc;
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: Strings.appName,
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: BlocBuilder<HomeEvent,HomeState>(
bloc: _homeBloc,
builder: (BuildContext context, HomeState state) {
if (state is HandleDrawerMenuClick) {
_onWidgetDidBuild(() {
print("clicked on drawer menu");
});
}
return Scaffold(
body: Center(
child: RaisedButton(
child: Text('ddddddddd'),
onPressed: () {
_homeBloc.dispatch(OnDrawerMenuClicked());
},
),
),
);
}),
);
}
#override
void dispose() {
_homeBloc.dispose();
super.dispose();
}
void _onWidgetDidBuild(Function callback) {
WidgetsBinding.instance.addPostFrameCallback((_) {
callback();
});
}
}
HomeBloc class:
class HomeBloc extends Bloc<HomeEvent, HomeState> {
#override
HomeState get initialState => HomeState();
#override
Stream<HomeState> mapEventToState(HomeEvent event) async* {
if (event is OnDrawerMenuClicked) {
yield HandleDrawerMenuClick();
}
}
}
HomeEvent class:
class HomeEvent extends Equatable {
HomeEvent([List props = const []]) : super(props);
}
class OnDrawerMenuClicked extends HomeEvent {
OnDrawerMenuClicked() : super([]);
#override
String toString() => 'OnDrawerMenuClicked clicked';
}
HomeState class:
class HomeState extends Equatable{
HomeState([List props = const[]]):super(props);
}
class HandleDrawerMenuClick extends HomeState{
#override
String toString()=>'HandleDrawerMenuClick';
}
i think problem is on HandleDrawerMenuClick class because when i debug application, debug can go into if statement on this line:
if (event is OnDrawerMenuClicked) {
yield HandleDrawerMenuClick();
}
and i think twice click on button couldn't trigger yield HandleDrawerMenuClick();
This seems to be an intended behavior because blocs doesn't re-emit on the same state as mentioned on this GitHub issue ticket.

bloc does not get initialized when using generic bloc provider

Here is the bloc (simplified):
import 'package:autobleidas_flutter/bloc/bloc_base.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:rxdart/rxdart.dart';
class LoginBloc extends BlocBase {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final PublishSubject<bool> loggedIn = PublishSubject<bool>();
final PublishSubject<bool> loading = PublishSubject<bool>();
}
Here is the bloc provider:
class BlocProvider<T> extends InheritedWidget {
final T bloc;
BlocProvider({Key key, Widget child, this.bloc})
: super(key: key, child: child);
static T of<T extends BlocBase>(BuildContext context) {
final type = _typeOf<BlocProvider<T>>();
return (context.inheritFromWidgetOfExactType(type) as BlocProvider).bloc;
}
static Type _typeOf<T>() => T;
#override
bool updateShouldNotify(InheritedWidget oldWidget) {
return true;
}
}
However, in the LoginScreen I cannot access the loggedIn Subject of the bloc. Here is how LoginScreen is opened from main and the bloc is passed to it:
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
localizationsDelegates: GlobalMaterialLocalizations.delegates,
supportedLocales: allTranslations.supportedLocales(),
home: BlocProvider<LoginBloc>(child: LoginScreen()), // <-------- HERE
);
}
}
Here is how I try to access it in the LoginScreen:
class _LoginScreenState extends State<LoginScreen> {
bool _isLoading = false;
#override
void didChangeDependencies() {
LoginBloc bloc = BlocProvider.of<LoginBloc>(context);
bloc.loggedIn.listen((isLoggedIn) => Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) => RegistrationScreen())));
bloc.loading.listen((state) => setState(() => _isLoading = state));
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
return Container();
}
the error:
The getter 'loggedIn' was called on null.
So why is the bloc null? How do I fix this?
In this line, BlocProvder expect a bloc.
home: BlocProvider<LoginBloc>(child: LoginScreen()),
You are not passing your bloc here.
Pass it like below:
home: BlocProvider<LoginBloc>(child: LoginScreen(),bloc: LoginBloc()),
BlocProvider<LoginBloc> means your defining a type of the bloc you are going to pass.

How to open a specific screen with quick_actions official plugin in flutter?

I implemented the Quick_actions plugin in my project and i want to open a specific screen but in the quickaction handler function the navigator doesnt work. whit a Try-Catch, the exception shows that the context showld be from a navigator, but im using the context of the navigatorKey of my MaterialApp.
if i put any other function like a print('some text') it works, the problem only happend when I try to use the navigator
Create the quick actions and add the handler function
createQuickActions() {
quickActions.initialize(
(String shortcutId) {
switch (shortcutId) {
case 'settings':
try {
Navigator.push(
MyApp.navigatorKey.currentContext,
MaterialPageRoute(
builder: (context) => SettingsScreen(sistemas),
),
);
} catch (e) {
print(e);
}
print('selected: $shortcutId');
break;
}
}
);
}
Initialice the quick actions
quickActions.setShortcutItems(
<ShortcutItem>[
const ShortcutItem(
type: 'settings',
localizedTitle: 'settings',
icon: 'settings',
),
],
);
All this code its in my SplashScreen because the plugin's documentation says that should be in an early state of the app
I expect that the app open the settings screen and print 'settings' but it opens the main screen and print 'settings' if the app its already open, but if its not it tries to open something and then close itself (not force close message)
In the following example,
Use MainView in quick action will open Login widget and directly click app will open Home widget
You can reference https://www.filledstacks.com/snippet/managing-quick-actions-in-flutter/ for detail
full code
import 'package:flutter/material.dart';
import 'package:quick_actions/quick_actions.dart';
import 'dart:io';
class QuickActionsManager extends StatefulWidget {
final Widget child;
QuickActionsManager({Key key, this.child}) : super(key: key);
_QuickActionsManagerState createState() => _QuickActionsManagerState();
}
class _QuickActionsManagerState extends State<QuickActionsManager> {
final QuickActions quickActions = QuickActions();
#override
void initState() {
super.initState();
_setupQuickActions();
_handleQuickActions();
}
#override
Widget build(BuildContext context) {
return widget.child;
}
void _setupQuickActions() {
quickActions.setShortcutItems(<ShortcutItem>[
ShortcutItem(
type: 'action_main',
localizedTitle: 'Main view',
icon: Platform.isAndroid ? 'quick_box' : 'QuickBox'),
ShortcutItem(
type: 'action_help',
localizedTitle: 'Help',
icon: Platform.isAndroid ? 'quick_heart' : 'QuickHeart')
]);
}
void _handleQuickActions() {
quickActions.initialize((shortcutType) {
if (shortcutType == 'action_main') {
Navigator.push(
context, MaterialPageRoute(builder: (context) => Login()));
} else if(shortcutType == 'action_help') {
print('Show the help dialog!');
}
});
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'QuickActions Demo', home: QuickActionsManager(child: Home()));
}
}
class Home extends StatelessWidget {
const Home({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(body: Center(child: Text('Home')));
}
}
class Login extends StatelessWidget {
const Login({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(body: Center(child: Text('Login')));
}
}

Using setState in separate BottomNotifcationBar class back to the main class

If I keep the bottomNotificationBar in the same class as the rest of the page, setState works properly and the buttons work properly.
If I move the bottomNotificationBar to another class, I cannot get the setState to work, because it needs to reference back to the main class. I've tried a few things, but I can't wrap my mind around this yet.
The error is:
The following assertion was thrown while handling a gesture:
setState() called in constructor:
The part that isn't working is marked near the bottom of this:
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'My Title',
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var selectedPageIndex = 0;
var pages = [ Page1(), Page2(), ];
#override
Widget build(BuildContext context) {
return new Scaffold(
body: pages[selectedPageIndex],
bottomNavigationBar:
MyClass().buildBottomNavigationBar(selectedPageIndex),
);
}
}
class MyClass {
BottomNavigationBar buildBottomNavigationBar(selectedPageIndex) {
return new BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
title: Text("Page1"),
icon: Icon(Icons.account_circle),
),
BottomNavigationBarItem(
title: Text("Page2"),
icon: Icon(Icons.account_circle),
),
],
onTap: (index) {
/////////////////////////////START OF SECTION///////////////////////////
_MyHomePageState().setState(() {
selectedPageIndex = index;
});
/////////////////////////////END OF SECTION///////////////////////////
},
currentIndex: selectedPageIndex,
);
}
}
--------------EDIT:----------------
Ok, now I have the following code below, and I am getting the following 2 things:
info:
The member 'setState' can only be used within instance members of subclasses of 'package:flutter/src/widgets/framework.dart'.
exception:
The following NoSuchMethodError was thrown while handling a gesture:
The method 'setState' was called on null.
Receiver: null
Tried calling: setState(Closure: () => Null)
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'My Title',
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
static void setIndex(BuildContext context, int _newIndex) {
_MyHomePageState state = context.ancestorStateOfType(TypeMatcher<_MyHomePageState>());
state.setState(() {
state.selectedPageIndex =_newIndex;
});
}
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var selectedPageIndex = 0;
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Container(),
bottomNavigationBar:
MyClass().buildBottomNavigationBar(context,selectedPageIndex),
);
}
}
class MyClass {
BottomNavigationBar buildBottomNavigationBar(context,selectedPageIndex) {
return new BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
title: Text("Page1"),
icon: Icon(Icons.account_circle),
),
BottomNavigationBarItem(
title: Text("Page2"),
icon: Icon(Icons.account_circle),
),
],
onTap: (index) {
MyHomePage.setIndex(context, index);
},
currentIndex: selectedPageIndex,
);
}
}
What you Require is CallBAck Function from the other class. As setState has to be called on object -_MyHomePageState.
With Class Constructors we pass the initial Data & got a Callback on SetState().
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'My Title',
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var selectedPageIndex = 0;
var pages = [
Page1(),
Page2(),
];
#override
Widget build(BuildContext context) {
return new Scaffold(
body: pages[selectedPageIndex],
bottomNavigationBar: MyClass(
selectedPageIndex: selectedPageIndex,
myFunc: _myFunc,
),
);
}
void _myFunc(int index) {
setState(() {
selectedPageIndex = index;
});
}
}
class MyClass extends StatelessWidget {
MyClass({this.selectedPageIndex, this.myFunc});
final int selectedPageIndex;
final Function myFunc;
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
title: Text("Page1"),
icon: Icon(Icons.account_circle),
),
BottomNavigationBarItem(
title: Text("Page2"),
icon: Icon(Icons.account_circle),
),
],
onTap: (index) {
/////////////////////////////START OF SECTION///////////////////////////
myFunc(index);
/////////////////////////////END OF SECTION///////////////////////////
},
currentIndex: selectedPageIndex,
);
}
}
class Page1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Text('1'),
),
);
}
}
class Page2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(child: Container(child: Text('3'),));
}
}
You should modify your MyHomePage by adding a static method into it so its state can be called from anywhere:
class MyHomePage extends StatefulWidget {
static void setIndex(BuildContext context, int _newIndex) {
_MyHomePageState state = context.ancestorStateOfType(TypeMatcher<_MyHomePageState>());
state.setState(() {
state.selectedPageIndex =_newIndex;
});
}
#override
_MyHomePageState createState() => new _MyHomePageState();
}
Then when you want to change the index call:
onTap (index) {
MyHomePage.setIndex(context, index);
}

How to change a State of a StatefulWidget inside a StatelessWidget?

Just testing out flutter. The code sample below is a very simple flutter app. The problem is that I don't know how to call the setState() function inside the TestTextState class in order to change the text each time when the change button is pressed.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Test app',
home: new Scaffold(
appBar: new AppBar(
title: new Text("Test"),
),
body: new Test(),
),
);
}
}
class Test extends StatelessWidget {
final TestText testText = new TestText();
void change() {
testText.text == "original" ? testText.set("changed") : testText.set("original");
}
#override
Widget build(BuildContext context) {
return new Column(
children: [
testText,
new RaisedButton(
child: new Text("change"),
onPressed: () => change(),
),
]
);
}
}
class TestText extends StatefulWidget {
String text = "original";
void set(String str) {
this.text = str;
}
#override
TestTextState createState() => new TestTextState();
}
class TestTextState extends State<TestText> {
#override
Widget build(BuildContext context) {
return new Text(this.widget.text);
}
}
I have approached this problem by initializing the _TestTextState as the final property of the TestText widget which allows to simply update the state when the change button is pressed. It seems like a simple solution but I'm not sure whether it's a good practice.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Test app',
home: new Scaffold(
appBar: new AppBar(
title: new Text("Test"),
),
body: new Test(),
),
);
}
}
class Test extends StatelessWidget {
final _TestText text = new _TestText();
#override
Widget build(BuildContext context) {
return new Column(
children: [
text,
new RaisedButton(
child: new Text("change"),
onPressed: () => text.update(),
),
]
);
}
}
class TestText extends StatefulWidget {
final _TestTextState state = new _TestTextState();
void update() {
state.change();
}
#override
_TestTextState createState() => state;
}
class _TestTextState extends State<TestText> {
String text = "original";
void change() {
setState(() {
this.text = this.text == "original" ? "changed" : "original";
});
}
#override
Widget build(BuildContext context) {
return new Text(this.text);
}
}
thier is no way to do so. any how you have to convert your StatelessWidget to StatefulWidget.
Solution based on your existing code
class Test extends StatelessWidget {
final StreamController<String> streamController = StreamController<String>.broadcast();
#override
Widget build(BuildContext context) {
final TestText testText = TestText(streamController.stream);
return new Column(children: [
testText,
new RaisedButton(
child: Text("change"),
onPressed: () {
String text = testText.text == "original" ? "changed" : "original";
streamController.add(text);
},
),
]);
}
}
class TestText extends StatefulWidget {
TestText(this.stream);
final Stream<String> stream;
String text = "original";
#override
TestTextState createState() => new TestTextState();
}
class TestTextState extends State<TestText> {
#override
void initState() {
widget.stream.listen((str) {
setState(() {
widget.text = str;
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Text(widget.text);
}
}
But it's not the best idea - to use non-final field inside Stateful Widget
P.S.
You can also use this - scoped_model

Resources