How do I access BuildContext outside of a stateful or stateless widget? - flutter

I created a class that extends the AppBar class in Flutter so I can reuse it whenever I need it.
My problem is how do I access the Stateful/Stateless widget build context?
class AppBarLayout extends AppBar {
static final AppController _appController = new AppController();
final GlobalKey<ScaffoldState> _scaffoldKey;
final String appBarTitle;
AppBarLayout(this.appBarTitle,this._scaffoldKey): super(
title: Text(appBarTitle),
leading: IconButton(
onPressed: () => _scaffoldKey.currentState.openDrawer(),
iconSize: 28,
icon: Icon(Icons.menu,color: Colors.white),
),
actions: <Widget>[
IconButton(
onPressed: () => _appController.signOut().then((_) {
_appController.navigateTo(context, new GoogleSignView());
}),
icon: Icon(Icons.account_box),
padding: EdgeInsets.all(0.0),
),
],
);
}

You would need to wrap your Scaffold in a Staless or Stateful widget, so you can get the context, e.g.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBarLayout(GlobalKey(debugLabel: 'someLabel'), appBarTitle: 'The Title', context: context,),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class AppBarLayout extends AppBar {
final GlobalKey<ScaffoldState> _scaffoldKey;
final String appBarTitle;
final BuildContext context;
AppBarLayout(this._scaffoldKey, {this.appBarTitle, this.context}): super(
title: Text(appBarTitle),
leading: IconButton(
onPressed: () => _scaffoldKey.currentState.openDrawer(),
iconSize: 28,
icon: Icon(Icons.menu,color: Colors.white),
),
actions: <Widget>[
IconButton(
onPressed: () {
print('Button pressed');
},
icon: Icon(Icons.account_box),
padding: EdgeInsets.all(0.0),
),
],
);
}
Here I'm using a very similar Widget of what you have.

Related

Flutter get text from widgets

I am coming from java and now making my very initial steps with flutter.
In my first application I am playing around with the layouts and the buttons and I am facing difficulties getting the button actions to work, probably due to my coming from from java.
In my app I have, among others, the following widgets:
Widget _buttonOne = RaisedButton(
onPressed: () {},
child: Text('Button One', style: TextStyle(fontSize: 20)),
);
final _textContainer =
const Text('Container One', textAlign: TextAlign.center);
Container(
padding: const EdgeInsets.all(8),
child: _textContainer,
color: Colors.teal[200],
),
Now I want to print the text of the button and textchild of the container. How do I achieve that?
CupertinoButton.filled(
child: Text('Button Two'),
onPressed: () {
print(_textContainer); // how do I print the text of the text widget here?
print (_buttonOne. ....); // on java there is a getText() method .... how does this work in flutter?
),
You can access the text of a Text widget by using the data property:
final _textContainer =
const Text('Container One', textAlign: TextAlign.center);
#override
Widget build(BuildContext context) {
return Scaffold(body:
Center(
child: CupertinoButton.filled(
child: Text('Button Two'),
onPressed: () =>
{print(_textContainer.data)},
)
)
);
}
In a similar fashion, you could access the content of the RaisedButton child:
final _raisedButtonText = const Text('Button One', style: TextStyle(fontSize: 20));
Widget _buttonOne = RaisedButton(onPressed: () {}, child: _raisedButtonText);
final _textContainer =
const Text('Container One', textAlign: TextAlign.center);
#override
Widget build(BuildContext context) {
return Scaffold(body:
Center(
child: CupertinoButton.filled(
child: Text('Button Two'),
onPressed: () {
print(_textContainer.data);
print(_raisedButtonText.data);
}
)
)
);
}
You can copy paste run full code below
You can use as to do type casting then access attribute data
code snippet
CupertinoButton.filled(
child: Text('Button Two'),
onPressed: () {
print(_textContainer.data);
print(((_buttonOne as RaisedButton).child as Text).data);
}),
output
I/flutter (30756): Container One
I/flutter (30756): Button One
full code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget _buttonOne = RaisedButton(
onPressed: () {},
child: Text('Button One', style: TextStyle(fontSize: 20)),
);
final _textContainer =
const Text('Container One', textAlign: TextAlign.center);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CupertinoButton.filled(
child: Text('Button Two'),
onPressed: () {
print(_textContainer
.data); // how do I print the text of the text widget here?
print(((_buttonOne as RaisedButton).child as Text).data);
}),
],
),
),
);
}
}

How to Fix problem child is'nt defined in drawer flutter

class Drawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
UserAccountsDrawerHeader(
accountName: Text("Anus"),
accountEmail: Text("Anus#gmail.com"),
currentAccountPicture: CircleAvatar(
backgroundImage: NetworkImage(
"https://images.unsplash.com/photo-1533227268428-f9ed0900fb3b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1158&q=80"),
),
),
ListTile(
leading: Icon(Icons.person),
title: Text("Account"),
subtitle: Text("Personal"),
trailing: Icon(Icons.edit),
),
ListTile(
leading: Icon(Icons.email),
title: Text("Email"),
subtitle: Text("anus#gmail.com"),
trailing: Icon(Icons.send),
),
],
),
);
}
}
How To Fix this Problem In flutter
I Want to Create Drawer but when i try to make drawer in new file for drawer The named parameter 'child' isn't defined.
You used same Widget at definition of that widget.
Here is a working code.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: _buildBody(),
floatingActionButton: FloatingActionButton(
onPressed: () {},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
Widget _buildBody() {
return Drawer();
}
}
class Drawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView(
padding: EdgeInsets.zero,
children: [
UserAccountsDrawerHeader(
accountName: Text("Anus"),
accountEmail: Text("Anus#gmail.com"),
currentAccountPicture: CircleAvatar(
backgroundImage: NetworkImage(
"https://images.unsplash.com/photo-1533227268428-f9ed0900fb3b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1158&q=80"),
),
),
ListTile(
leading: Icon(Icons.person),
title: Text("Account"),
subtitle: Text("Personal"),
trailing: Icon(Icons.edit),
),
ListTile(
leading: Icon(Icons.email),
title: Text("Email"),
subtitle: Text("anus#gmail.com"),
trailing: Icon(Icons.send),
),
],
);
}
}
your issue stems from the fact that you used the Name Drawer for the class (your custom widget), which means that the compiler won't know which Drawer widget to use, either the Drawer from the material library or the Drawer that you just defined.
to fix this issue just change the name of the class to AppDrawer or any other name and the compiler will start recognizing that you're trying to use the Drawer defined in the material library.

I need hashtag sign in url launcher TEL, but it gets removed automatically

i want to dial this number as it is, in flutter url launcher but it removes the hashtag sign in the last bit of the String,
onTap: () {
String no = '*477*4*1#';
launch('tel:$no');
},
You can copy paste run full code below
You can use Uri.encodeComponent('*477*4*1#');
code snippet
onPressed: () {
String no = Uri.encodeComponent('*477*4*1#');
launch('tel:$no');
},
working demo
full code
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('click'),
onPressed: () {
String no = Uri.encodeComponent('*477*4*1#');
launch('tel:$no');
},
),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
You need to use URL Encoding for special character so # is equals to \%23.

how to change properties of list tile from its onTap() function in flutter?

leading: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
// There I want to change title, background color etc
},
),
title: Text('title'),
dense: false,
),
I want to change properties of list tile from its own onTap()
You can copy paste run full code below
You can use StatefulWidget and call setState inside onTap
code snippet
class _ListTileCustomState extends State<ListTileCustom> {
String _title = "title";
Color _color = Colors.blue;
#override
Widget build(BuildContext context) {
return ListTile(
tileColor: _color,
leading: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
setState(() {
_title = "changed";
_color = Colors.red;
});
},
child: Icon(Icons.add)),
title: Text(_title),
dense: false,
);
}
}
working demo
full code
import 'package:flutter/material.dart';
class ListTileCustom extends StatefulWidget {
#override
_ListTileCustomState createState() => _ListTileCustomState();
}
class _ListTileCustomState extends State<ListTileCustom> {
String _title = "title";
Color _color = Colors.blue;
#override
Widget build(BuildContext context) {
return ListTile(
tileColor: _color,
leading: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
setState(() {
_title = "changed";
_color = Colors.red;
});
},
child: Icon(Icons.add)),
title: Text(_title),
dense: false,
);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ListTileCustom(),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
you can define one variable before build a method and use that variable to the title of listtile and when you ontap then change the state using setState and It will change the title of your listtie.
leading: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
setState((){
title="your text"
});
},
),
title: Text(title),
dense: false,
),
here title is variable
and ListTile also has property of onTap

MaterialPageRoute goes to a new page without the appbar using it with bottomNavigationBar

I have an app with three routes and uses the bottomNavigationBar to navigate between them. In one of the routes I have a button in the page that will also navigate to one of the pages.
Heres my main page
import 'package:flutter/material.dart';
import 'page_two.dart';
import 'page_three.dart';
void main() {
return runApp(MyApp());
}
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
List<Widget> _widgetOptions = <Widget>[
Text('Main'),
PageTwo(),
PageThree(),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home, color: Colors.black),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business, color: Colors.black),
title: Text('Business'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business, color: Colors.black),
title: Text('Business'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
Page Two
import 'package:flutter/material.dart';
import 'main.dart';
class PageTwo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: RaisedButton(
child: Text('Go page 1'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MyApp()),
);
},
),
);
}
}
and Page Three with a button that navigates to page two
import 'package:flutter/material.dart';
import 'page_two.dart';
class PageThree extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: RaisedButton(
child: Text('Go page 1'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PageTwo()),
);
},
),
);
}
}
When I press the button on Page Three, it will go to Page Two without the AppBar and the BottomNavigationBar
Use GlobalKey and In PageTwo Widget call MyStatefulWidgetState's _onItemTapped function
You can see working demo and full code below
code snippet
final scakey = new GlobalKey<_MyStatefulWidgetState>();
...
child: Text('Go page 2'),
onPressed: () {
scakey.currentState._onItemTapped(1);
full code
import 'package:flutter/material.dart';
void main() {
return runApp(MyApp());
}
final scakey = new GlobalKey<_MyStatefulWidgetState>();
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(key: scakey),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
final myKey = new GlobalKey<_MyStatefulWidgetState>();
List<Widget> _widgetOptions = <Widget>[
Text('Main'),
PageTwo(),
PageThree(),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: myKey,
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home, color: Colors.black),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business, color: Colors.black),
title: Text('Business'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business, color: Colors.black),
title: Text('Business'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
class PageTwo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: RaisedButton(
child: Text('Go page 1'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MyApp()),
);
},
),
);
}
}
class PageThree extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: RaisedButton(
child: Text('Go page 2'),
onPressed: () {
scakey.currentState._onItemTapped(1);
/*Navigator.push(
context,
MaterialPageRoute(builder: (context) => PageTwo()),
);*/
},
),
);
}
}
When using navigation bar to navigate between pages, you are tapping on BottomNavigationBarItem to change the index by calling setState() and as the result, build method is triggered with a new _selectedIndex and that index is used to render your appropriate widget.
_widgetOptions.elementAt(_selectedIndex)
Navigator.push on the other hand is just pushing a new route on top of the navigation stack. You are not getting an AppBar or BottomNavigationBar since you don't have them on PageTwo. What I would recommend you is to create a callback function in PageTwo and call that function on button tap. You can now use that callback in MyStatefulWidget to change the index with setState. Here is an example
Declare a final like below in your pages.
final void Function(int index) pageChanged;
In the onTap event of your button, call this function.
widget.pageChanged(1); // PageTwo
In MyStatefulWidget, when you are creating pages, pass the function.
PageTwo(pageChanged:(index){
setState(){_selectedIndex = index;}
});