Flutter: Calling Function from another Class State - flutter

My app allows people to post text and switch between different pages on a navbar. On the users page, there is a button, when clicked, will show an overlay so the user can create a post. The overlay includes a back button that calls a function to close the overlay. I want to keep the navbar available at the bottom so user can back out of the post that way if they want to.
The problem is, when the user uses the navbar, the overlay does not close because the close overlay function is on the user page and the navbar page does not have access to it.
How do I give another class on another dart file access to a method or function? If you are able to answer, can you please use my code instead of another example to help me follow better? Thank you.
User Page File #1
class UserPage extends StatefulWidget {
#override
_UserPageState createState() => _UserPageState();
}
class _UserPageState extends State<UserPage> {
OverlayEntry? entry;
#override
Widget build(BuildContext context) {
return Scaffold(
ElevatedButton(
child: const Text('New Post'),
onPressed: showOverlay,
),
),
}
void showOverlay() {
(...)
}
void closeOverlay() {
entry?.remove();
entry = null;
}
}
Nav Bar File #2 (Need help with "OnTap")
class Nav extends StatefulWidget {
const Nav({Key? key}) : super(key: key);
#override
_NavState createState() => _NavState();
}
class _NavState extends State<Nav> {
int currentTab = 1; // makes the home page the default when loading up the app
#override
void initState() {
super.initState();
}
List<Widget> tabs = <Widget>[
const Other(),
const Home(),
const UserPage(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: tabs.elementAt(currentTab),
),
// BOTTOM NAVIGATION BAR
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentTab,
onTap: (value) {
setState(() => currentTab = value);
const _UserPageState().closeOverlay(); //HERE IS WHERE I NEED HELP WITH THE CODE
},
items: const [
BottomNavigationBarItem(
label: 'Other',
),
BottomNavigationBarItem(
label: 'Home',
),
BottomNavigationBarItem(
label: 'User Page',
),
],
),
);
}
}

You can try to make your _UserPageState public by removing - from it, and then call it UserPageState().closeOverlay();

Related

How to call init method or specific function again when we click on already activated bottom menu

I have implemented following BottomNavigation
class AppMenu extends StatefulWidget {
const AppMenu({Key? key}) : super(key: key);
#override
State<AppMenu> createState() => _AppMenuState();
}
class _AppMenuState extends State<AppMenu> {
int current = 0;
final List<String> titles = [
"Home 1",
"Home 2"
];
final List<Widget> views = [
const HomeView1(),
const HomeView2(),
];
final List<String> icons = [
"icon_1",
"icon_2",
];
final List<String> barTitles = ["Home1", "Home2"];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: HomeAppBar(
title: titles[current],
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: (index) {
setState(() {
current = index;
});
},
selectedItemColor: const Color(0xff6B6B6B),
showUnselectedLabels: true,
showSelectedLabels: true,
unselectedItemColor: const Color(0xff6B6B6B),
selectedLabelStyle: const TextStyle(fontSize: 12),
unselectedLabelStyle: const TextStyle(fontSize: 12),
items: views.map((e) {
final itemIndex = views.indexOf(e);
return BottomNavigationBarItem(
icon: Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Image.asset(
"assets/images/${icons[itemIndex]}${itemIndex == current ? "" : "_disabled"}.png",
width: 25,
),
),
label: barTitles[itemIndex],
);
}).toList()),
body: Column(
children: [
Expanded(child: views[current]),
],
),
);
}
}
Now it works perfect when I click on home1 and home2 bottom menu and it shows respected widget and load all the content which I have wrote on initState of home1 and home2 but now assume that I am on home1 and if I click again home1 then it is not calling initState again.
I want to call initState or specific function if user click on that menu even if it is selected.
Is there any way to do it?
You can create a initialize or initXXX function to initialize something in initState or somewhere. If parent widget call setState(), then child widget will call didUpdateWidget().
void initialize() {
// do something
}
Call initialize() in initState().
void initState() {
super.initState();
initialize();
}
Call initialize() in didUpdateWidget() of page(child widget).
#override
void didUpdateWidget(covariant PageTest oldWidget) {
super.didUpdateWidget(oldWidget);
initialize();
}
To handle the case in a simple way. You can add your method in onTap of BottomNavigationBar and then pass your data down to the widget tree.
It's only a demonstration to handle your case, you can adjust it with your own liking
For example
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: (index) {
if(current == index){
foo = yourMethodHere();
}
setState(() {
current = index;
});
},
Pass the variable in the tree
List<Widget> get views => [
HomeView1(foo),
HomeView2(foo),
];

Comunication between drawer menu and pageview [flutter]

I would like to: when user tapped drawer menu item, change the pageview's index which is located at main screen.
I tried to change index from another file but I couldn't
drawer menu code
InkWell(
onTap: () {
debugPrint("Tapped");
HomeApp().openMyGloves();
},
)
openMyGloves()
class HomeApp extends StatefulWidget {
const HomeApp({Key? key}) : super(key: key);
#override
State<HomeApp> createState() => _HomeAppState();
void openMyGloves() {
_HomeAppState()._openMyGloves();
}
}
class _HomeAppState extends State<HomeApp> {
class _HomeAppState extends State<HomeApp> {
int simdikiIndex = 1;
late List<Widget> tumSayfalar;
late Blog blogSayfa;
late MyGloves gloveSayfa;
late HomePage homeSayfa;
late final controller;
#override
void initState() {
blogSayfa = const Blog();
gloveSayfa = const MyGloves();
homeSayfa = const HomePage();
tumSayfalar = [blogSayfa, homeSayfa, gloveSayfa];
controller = PageController();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
drawer: const DrawerMenu(),
bottomNavigationBar: bottomNav(),
body: PageView(
/// [PageView.scrollDirection] defaults to [Axis.horizontal].
/// Use [Axis.vertical] to scroll vertically.
controller: controller,
children: <Widget>[blogSayfa, homeSayfa, gloveSayfa],
onPageChanged: (page) {
setState(() {
simdikiIndex = page;
});
}));
}
BottomNavigationBar bottomNav() {
return BottomNavigationBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Image.asset(
"assets/images/info.png",
scale: 2,
),
label: "HAKKIMIZDA",
),
BottomNavigationBarItem(
icon: Image.asset(
"assets/images/home.png",
scale: 2,
),
label: "ANA SAYFA",
),
BottomNavigationBarItem(
icon: Image.asset(
"assets/images/gloves.png",
scale: 2,
),
label: "ELDİVENLERİM",
),
],
onTap: (index) {
setState(() {
simdikiIndex = index;
controller.jumpToPage(index);
});
},
currentIndex: simdikiIndex,
);
}
void _openMyGloves() {
controller.jumpToPage(2);
}
}
}
note: I got
Late Initialization Error
for controller.for controller.for controller.for controller.for controller.for controller.for controller.for controller.for controller.for controller.for controller.for controller.
HomeApp().openMyGloves();
Here you are calling openMyGloves() from a new instance of the HomeApp which is not the one that exists in the widget tree
to solve this you have to access the same HomeApp which is built in the widget tree, this will be done by these steps:
1- Make _HomeAppState not private by removing the underscore _
2- define a global key with the HomeAppState in the parent widget of the HomeApp and pass it to HomeApp widget
static final GlobalKey<HomeAppState> homeAppKey = GlobalKey();
then
child: HomeApp(key: homeAppKey),
now you can call openMyGloves() using this key like this
ParentWidget.homeAppKey.currentState?.openMyGloves();
ParentWidget is the parent class of HomeApp in which you define the key and pass it to HomeApp
It'll be way easier if you pass the page view value you want to access to the parent widget, in your case HomeApp(), the code should look like this:
class DrawerMenu extends StatefulWidget {
Function pageViewIndex;
DrawerMenu({required this.pageViewIndex});
#override
_DrawerMenuState createState() => _DrawerMenuState();
}
class _DrawerMenuState extends State<DrawerMenu> {
#override
Widget build(BuildContext context) {
return DrawerMenuItem(
child: Text(item),
onPressed: () {
widget.pageViewIndex(x);
// Where x equals the value you want to pass to HomeApp()
}
);
}
}
Once you did that, you can now read that value from HomeApp() by using a function like this:
// This function goes to HomeApp()
void function(pageViewIndex) {
setState(() {
simdikiIndex = pageViewIndex;
});
}
A different approach would be working with Provider, but if you're not familiarized with it the approach I just gave you should do the trick

How to apply MVC or design pattern in flutter?

I am trying to include biometric authentication using local_auth package. This is used when the app starts. The fingerprint is used to determine whether the user is the owner of the phone. If it is confirmed, they will be taken to the home page. The below code works but what I would like to apply on the below code is MVC or design pattern. Can someone guide me?
class LoginOptionState extends State<LoginOption> {
final LocalAuthentication auth = LocalAuthentication();
String _authorized = 'Not Authorized';
#override
Widget build(BuildContext context) {
return Scaffold(
body: new Container(
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new Column(
children: <Widget>[
Text("Touch ID"),
SizedBox(height: 10),
GestureDetector(
child: Image.asset(
"assets/",
),
onTap: _authenticate),
],
),
],
),
)));
}
Future<void> _authenticate() async {
bool authenticated = false;
try {
authenticated = await auth.authenticateWithBiometrics(
localizedReason: 'Scan your fingerprint to authenticate',
useErrorDialogs: true,
stickyAuth: false);
} on PlatformException catch (e) {
print(e);
}
if (!mounted) return;
setState(() {
_authorized = authenticated
? Navigator.pushNamed(context, homePageViewRoute)
: 'Not Authorized';
});
}
}
Use the excellent library by Greg Perry mvc_pattern. Quick start sample code and explanation is provided on the link.
Here is a quick start example of the classic counter app, from the link above:
The view:
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: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
// Fields in a Widget subclass are always marked "final".
static final String title = 'Flutter Demo Home Page';
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final Controller _con = Controller.con;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
widget.title,
),
Text(
'${_con.counter}',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(
_con.incrementCounter
);
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Controller class:
class Controller extends ControllerMVC {
/// Singleton Factory
factory Controller() {
if (_this == null) _this = Controller._();
return _this;
}
static Controller _this;
Controller._();
/// Allow for easy access to 'the Controller' throughout the application.
static Controller get con => _this;
int get counter => _counter;
int _counter = 0;
void incrementCounter() => _counter++;
}
Now the above code refactored, to add a model:
int get counter => Model.counter;
void incrementCounter() {
/// The Controller knows how to 'talk to' the Model. It knows the name, but Model does the work.
Model._incrementCounter();
}
And finally the model class:
class Model {
static int get counter => _counter;
static int _counter = 0;
static int _incrementCounter() => ++_counter;
}
However make sure you upgrade flutter to version 1.13.0. At least for me, I was getting several build errors in lower versions.
Karee is a set of tools that implementes MVC design in Flutter. It help you to manage your Controllers, your routes, your screens and more. Refer to karee github wiki to get documentation.
You Can use Karee . It supports Flutter 2.X.X
To installation run
npm install -g karee
Then karee create
After creating a New Flutter project based on Karee you can add new controller
Sample Code
Creating à New controller
Karee generate --controller --path auth Authentication
Generated file under lib/app/controllers/auth/AuthenticationController
#Controller
class AuthenticationController {
login(username, password) {
/// Your custom code
}
}
Add route
Route.on('/login', 'AuthenticationController#login');
Use in your Screen
var authUser = KareeRouter.goto('/login', parameter:[username, password]);

Flutter, How to update a text in an item in listview after updating the content from it's detail view?

I am following this link,
https://medium.com/…/developing-for-multiple-screen-sizes-a…
to create a master detail ipad application.
I have a scenario, there is a text field and button in detail page. When i change the text field value and press the button, the listview item (in left side) at that specific index also should be updated. can somebody suggest a work around?
You can return the edited object using Navigator.pop(context,object) to the Navigator.push() caller. I wrote an example app for you.
the data class:
class Item {
final String name;
Item(this.name);
}
the home page, where I display the item:
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Item item = Item('ali2236');
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: Center(
child: Column(
children: <Widget>[
Text(item.name),
FlatButton(
child: Text('edit'),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return ItemEditingPage(
item: item,
callbackFunction: (editedItem){
setState(() {
item = editedItem;
});
},
);
}));
},
),
],
),
),
),
);
}
}
and the editing page:
class ItemEditingPage extends StatefulWidget {
final Item item;
final void Function(Item item) callbackFunction;
const ItemEditingPage({Key key, this.item, this.callbackFunction}) : super(key: key);
#override
_ItemEditingPageState createState() => _ItemEditingPageState();
}
class _ItemEditingPageState extends State<ItemEditingPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: FlatButton(
child: Text('change name to aligator'),
onPressed: () {
///
/// if the name is [final], you create a new Item and pass it back
///
Item item = Item('aligator');
widget.callbackFunction(item);
///
/// if the name is not final you can just change it on the current object
///
//widget.item.name = 'aligator';
//widget.callbackFunction(widget.item);
},
),
),
),
);
}
}
edit: used a callback function instead of Navigator.pop() to notify the showcase page.

Flutter - Send params in Bottom Navigation Bar

I have two different pages. One of these is a form, the other is a list of elements. If you swipe one of the elements to left can edit. I need to send the element data (from the list) to first page (the form)
When the application starts the data is null, if it come from the element list isn´t null.
The navigation bar code is:
import 'package:flutter/material.dart';
import 'package:datameter/screens/configuration/form/partials/home_page.dart';
import 'package:datameter/screens/configuration/list/datameters.dart';
import 'package:datameter/locations/locations.dart';
class Navigation extends StatefulWidget {
final item;
Navigation(
{Key key,
this.item})
: super(key: key);
#override
State<StatefulWidget> createState() {
return _NavigationState();
}
}
class _NavigationState extends State<Navigation> {
int currentIndex = 0;
List<Widget> children = [
HomePageScreen(datameter: widget.item), //ERRROR HERE
DatametersPageScreen(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: children[currentIndex],
bottomNavigationBar: new Theme(
data: Theme.of(context).copyWith(
primaryColor: Colors.blue[700],
textTheme: Theme.of(context)
.textTheme
.copyWith(caption: new TextStyle(color: Colors.black)),
),
child: BottomNavigationBar(
onTap: onTabTapped,
currentIndex: currentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.add),
title: Text(DemoLocalizations.of(context).text('new-device')),
),
BottomNavigationBarItem(
icon: Icon(Icons.list),
title: Text(DemoLocalizations.of(context).text('show-all')),
),
],
)),
);
}
void onTabTapped(int index) {
setState(() {
currentIndex = index;
});
}
}
When I try to send params from elements list (using navigation bottom bar) to the form, returns
Static only members can be accessed in initializers.
Does anybody know how to fix this?
_NavigationState Object is not constructed so you cannot access "widget" getter yet because its not initialized and its not static property either.
change
List<Widget> children = [
HomePageScreen(datameter: widget.item), //ERRROR HERE
DatametersPageScreen(),
];
to
List<Widget> _children() =>
[
HomePageScreen(datameter: widget.item),
DatametersPageScreen(),
];
and then in your build change
#override
Widget build(BuildContext context)
{
final List<Widget> children = _children();
return Scaffold
(
//code
body: children[currentIndex],
//code
);
}