flutter: button onpress action function continuously calling - dart

When I add onPress function to the button, loadMore function is called when the app starts. And It continuously increate the page number. I couldn't find what is wrong.
Widget header(){
return new Container(
color: Colors.blue[900],
height: 40.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new IconButton(
icon: new Icon(
Icons.arrow_back,
),
onPressed: loadMore(page: page = page + 1)
),
],
),
);
}

You have a function invocation, but you should pass a function reference
onPressed: () => loadMore(page: page = page + 1)
If loadMore didn't have parameters (actually when it has the same parameters the caller uses to invoke the method), you could use
onPressed: loadMore
to pass the function reference without creating an closure.
With your code
onPressed: loadMore(page: page = page + 1)
loadMore(...) will be called every time Flutter runs build()

Related

how can I make BottomNavigationBar stick on top of keyboard flutter

I am trying to make a simple chat app, so I created a scaffold and my body, will be the messages and my bottomNavigationBar would be my typing field and sending icon.
I added a text field but when typing the navigation bar is hidden by the keyboard.
this is the code of my BottomNavigationBar :
bottomNavigationBar: new Container(
height: ScreenSize.height/12,
/*color: Colors.red,*/
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
new Container(
child: new Icon(Icons.send),
width:ScreenSize.width/6,
),
],
),
new Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Material(
child: new Container(
child: new TextField(
autofocus: false,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(9.0),
border: InputBorder.none,
hintText: 'Please enter a search term',
),
),
width:ScreenSize.width*4/6,
),
elevation: 4.0,
/*borderRadius: new BorderRadius.all(new Radius.circular(45.0)),*/
clipBehavior: Clip.antiAlias,
type: MaterialType.card,
)
],
),
new Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
new Container(
child: Text('HELLO C1'),
color: Colors.green,
width:ScreenSize.width/6,
),
],
)
],
),
),
here is how it looks when focused :
if you use a Stack on your Scaffold's body, instead of bottomNavigationBar, your nav will push up above the keyboard. even if you fix to the bottom with a Positioned:
Positioned(
bottom: 0.0,
left: 0.0,
right: 0.0,
child: MyNav(),
),
simply wrap your bottom navigation bar with Padding and set it to MediaQuery.of(context).viewInsets,
bottomNavigationBar: Padding(
padding: MediaQuery.of(context).viewInsets,
child: ChatInputField(),
),
Literally just worked through the same issue. Given the code i was refactoring, this worked like a charm. Peep the github link, review his change and apply. Couldn't be much more straight forward: https://github.com/GitJournal/GitJournal/commit/f946fe487a18b2cb8cb1d488026af5c64a8f2f78..
Content of the link above in case the link goes down:
(-)BottomAppBar buildEditorBottonBar(
(+)Widget buildEditorBottonBar(
BuildContext context,
Editor editor,
EditorState editorState,
BottomAppBar buildEditorBottonBar(
folderName = "Root Folder";
}
*REPLACE* return BottomAppBar(
elevation: 0.0,
color: Theme.of(context).scaffoldBackgroundColor,
child: Row(
children: <Widget>[
FlatButton.icon(
icon: Icon(Icons.folder),
label: Text(folderName),
onPressed: () {
var note = editorState.getNote();
editor.moveNoteToFolderSelected(note);
},
)
],
mainAxisAlignment: MainAxisAlignment.center,
*WITH THE WRAPPER* return StickyBottomAppBar(
child: BottomAppBar(
elevation: 0.0,
color: Theme.of(context).scaffoldBackgroundColor,
child: Row(
children: <Widget>[
FlatButton.icon(
icon: Icon(Icons.folder),
label: Text(folderName),
onPressed: () {
var note = editorState.getNote();
editor.moveNoteToFolderSelected(note);
},
)
],
mainAxisAlignment: MainAxisAlignment.center,
),
),
);
}
class StickyBottomAppBar extends StatelessWidget {
final BottomAppBar child;
StickyBottomAppBar({#required this.child});
#override
Widget build(BuildContext context) {
return Transform.translate(
offset: Offset(0.0, -1 * MediaQuery.of(context).viewInsets.bottom),
child: child,
);
}
}
I achieved this by a mix of two things I found separated in the web:
1 - Inside the Scaffold, I put other with only a bottomNavigationBar with a empty Container. For some reason, this trick push all my real bottomNavigationBar up to the top of the keyboard.
Scaffold(
bottomNavigationBar: Container(
height: 0,
),
body: Scaffold(
body: MyWidget(
But, I did not want all the content up, so I got that Package:
2 - I added flutter_keyboard_visibility: ^5.1.0 from
https://pub.dev/packages/flutter_keyboard_visibility
With this Package, you can do anything you want in response to keyboard visibility - is up to you. In my case, I made all content of my real bottomNavigationBar disappear except the textfield, which stay on the top of the keyboard:
[TextFormField] // dont go away,
//The others:
KeyboardVisibilityBuilder(builder: (context, visible) {
return Column(
children: [
visible
? SizedBox(
height: 0,
)
: OtherWidgets(
If you need some kind of button; you can do:
Scaffold(
bottomNavigationBar: bottomNavigationBar,
floatingActionButton: ExampleButton(
text: 'Hello',
),
body: body,
),
You can apply further customizations on the Floating Action Button using parameters in the Scaffold.
There is a simple way to do this if you want to really need to use the bottom navigation bar of the scaffold to put your widgets in rather than put it on a stack. Just wrap your scaffold with another scaffold and it should solve the problem.
return Scaffold(
body: Scaffold(
bottomNavigationBar: yourBottomNavigationBarWidget(),
body: yourBody(),
This works best especially when the height of your widget changes dynamically (because the text user types may introduce multiple lines) and you want the body to resize accordingly. A body in the stack, as suggested by many, will require a bottom padding to be visible over the text field and need to change dynamically as user types which is difficult to handle when you have multiple widgets sitting in and around the text field.

ListView does not refresh whereas attached list does (Flutter)

I'm trying to get familiar with flutter and I'm facing some weird case. I want to build a dynamic ListView where a + button allows to add elements. I wrote the following State code:
class MyWidgetListState extends State<MyWidgetList> {
List<Widget> _objectList = <Widget>[
new Text('test'),
new Text('test')
];
void _addOne() {
setState(() {
_objectList.add(new Text('test'));
});
}
void _removeOne() {
setState(() {
_objectList.removeLast();
});
}
#override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new ListView(
shrinkWrap: true,
children: _objectList
),
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new IconButton(
icon: new Icon(Icons.remove_circle),
iconSize: 36.0,
tooltip: 'Remove',
onPressed: _objectList.length > 2 ? _removeOne : null,
),
new IconButton(
icon: new Icon(Icons.add_circle),
iconSize: 36.0,
tooltip: 'Add',
onPressed: _addOne,
)
],
),
new Text(_objectList.length.toString())
],
);
}
}
My problem here is that the ListView is visually stuck with the 2 elements I initialized it with.
Internally the _objectList is well managed. For testing purpose I added a simple Text widget at the bottom that shows the size of the list. This one works fine when I click the Add/Remove buttons and it gets properly refreshed. Am I missing something?
Flutter is based around immutable data. Meaning that if the reference to an object didn't change, the content didn't either.
The problem is, in your case you always send to ListView the same array, and instead mutate its content. But this leads to ListView assuming the list didn't change and therefore prevent useless render.
You can change your setState to keep that in mind :
setState(() {
_objectList = List.from(_objectList)
..add(Text("foo"));
});
Another Solution!!
Replace ListView with ListView.builder
Code:
ListView.builder(
itemBuilder: (ctx, item) {
return _objectList[item];
},
shrinkWrap: true,
itemCount: _objectList.length,
),
Output:

How to give onPressed a method in custom button flutter

i have custom button and i want to add method in my onpressed from my parameter,unfortunately i'm still stuck what type should i use in my custom button parameter, curently i hard code the onpressed method and pass a string route to navigate the problem is not all my button is made to navigate there is one button to logout, i already used void in my parameter but it's still doesn't work.
here is my custom button
FlatButton buttonMenu(String title, IconData icon, String route) {
return new FlatButton(
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(25.0)),
color: Colors.blueGrey,
child: new Container(
height: 50.0,
width: 120.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Padding(
padding: EdgeInsets.only(right: 10.0),
child: new Icon(
icon,
color: Colors.white70,
size: 30.0,
),
),
new Text(
title,
style: new TextStyle(fontSize: 20.0, fontWeight: FontWeight.w300),
),
],
),
),
onPressed: () {
Navigator.of(context).pushNamed('/$route');
},
);
}
}
and here is how i call the custom button
buttonMenu('MVP Arc', Icons.star, 'EmployeeScreen')
and what i want is i can send method in my parameter to my onpressed so it will be like this
buttonMenu('MVP Arc', Icons.star, myMethod())
my method for navigate
void _navigate(String route){
Navigator.of(context).pushNamed('/$route');
}
the onPressed type is VoidCallback so make your parameter like this this type.
FlatButton buttonMenu(String title, IconData icon, VoidCallback onCustomButtonPressed )
and when you call it pass a reference to it
buttonMenu('MVP Arc', Icons.star, myMethod)
and don't forget to do the same in your function body
onPressed: onCustomButtonPressed ,
UPDATE:
if you want to pass parameters you have to declare your own Signature :
Example in your case :
typedef VoidNavigate = void Function(String route);
and then use this new type
FlatButton buttonMenu(String title, IconData icon, VoidNavigate onCustomButtonPressed)
call it like this
onPressed: (){onCustomButtonPressed},
and you pass your function like this
buttonMenu('MVP Arc', Icons.star, _navigate('EmployeeScreen'))
or you can just do it like this as #Kirill Shashov said
buttonMenu('MVP Arc', Icons.star, () {_navigate('EmployeeScreen');})
The accepted answer for passing parameters no longer seems to work on newer versions of Flutter.
In case someone searches for a similar issue, the modifications below worked for me.
Modifying Raouf Rahiche's answer, the working method as of 2019. October 28 is as follows.
Update: added mising flutter version number below
Flutter version: 1.9.1+hotfix.5
Dart version: 2.5.0
Using the existing typedef:
typedef VoidNavigate = void Function(String route);
Using the type changed in a small way, you have to pass the parameter to the button separately.
FlatButton buttonMenu(
String title,
IconData icon,
VoidNavigate onCustomButtonPressed,
String route //New addition
) {
...
}
Calling it now:
onPressed: () { onCustomButtonPressed(route); },
Passing the function:
buttonMenu('MVP Arc', Icons.star, _navigate, 'EmployeeScreen')
Here is what worked for me:
Flutter Version : 2.6.0
Dart Version : 2.15.0
in my customButton function:
Widget customButton(VoidCallback onPressed)
{
....
onPressed : onPressed.call(),
}
and no other changes needed.
the only we need to do is add .call() to the end of the function to be void.
onPressed: ()=>onPressed to onPressed: ()=>onPressed.call()

Second page in flutter naviagtion flow is not full size

I am trying to make a basic "new activity" in flutter
I have followed the flutters docs and made a button that navigates to the next page with:
Navigator.push(context, new MaterialPageRoute(builder: (context)=> new HomePage(title: title,)));
This is the build method of the 2nd page/activity (HomePage.dart)
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Home"),
textTheme: orbitTextTheme),
body: new Center(
child: new Container(
color: Color.orange,
height: 150.0,
width: 150.0,
child:new Column(
children: <Widget>[
new TextField(
controller: _textController,
decoration: new InputDecoration(
hintText: "Try to type?"
),
)
],
),
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _newReg,
tooltip: 'New reg', //externalize
backgroundColor: Color.orange,
child: new Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
BUT, the second page is not full size: (Notice the missing floating action button)
Can anyone tell me what am doing wrong?
UPDATE
Was calling the Navigator.push( in a callback from googleSignIn (which presents a modal). So putting in a small delay fixed the bug. Thanks!
This graphical bug happens whenever you pop the last visible route before pushing a new one.
Consider using pushReplacement or pushReplacementNamed instead.

Dart / Flutter: building widget from data obtained using async method

I want to use a LietView.build populated by Widgets that obtain the data from an async method, before the widget is built. Here is my function that collects the data from a website:
fetchBasicData(String URL) async {
final response = await http.get(URL);
var document = parse(response.body);
var result = new List<dom.Node>();
result = document.getElementsByClassName('datas-nev');
var dogName = result[0].nodes[1].toString();
result = document.getElementsByClassName('datas-tipus');
var dogBreed = result[0].nodes[1].toString();
result = document.getElementsByClassName('datas-nem');
var dogGender = result[0].nodes[1].toString();
result = document.getElementsByClassName('datas-szin');
var dogColor = result[0].nodes[1].toString();
result = document.getElementsByClassName('datas-kor');
var dogAge = result[0].nodes[1].toString();
result = document.getElementsByClassName('pirobox_gall');
String imageLink;
imageLink = urlPrefix + result[0].nodes[0].attributes.values.first;
return new Dog.basic(
URL,
dogName,
dogBreed,
dogGender,
dogColor,
dogAge,
imageLink);
}
The function is executed and gathers the data, but the widget building fails with type '_Future' is not a subtype of type 'Widget' of 'child' where
Here is the function that is supposed to build the widget:
buildBasicWidget(String URL) async {
Dog myDog = await fetchBasicData(URL);
return new SizedBox(
width: 500.0,
height: 400.0,
child: new Card(
child: new Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
//Header image row
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Expanded(
child: new FutureBuilder(
future: fetchPortrait(myDog.imageLink),
builder: (context, snapshot) {
if (snapshot.hasData) {
return new Image.network(
snapshot.data,
fit: BoxFit.scaleDown,
);
} else {
if (snapshot.hasError) {
return new Text('Hiba');
}
}
return new Center(
child: new CircularProgressIndicator(),
);
}))
],
), //Header image row
new Row(
children: <Widget>[
new Expanded(
child: new Text(
dogName,
style: new TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
color: Colors.black),
))
],
),
new Row(
children: <Widget>[
new Expanded(
child: new Text(myDog.dogColor +
'színű, ' +
myDog.dogBreed +
' ' +
myDog.dogGender))
],
),
new Row(
children: <Widget>[
new Expanded(child: new Text('Kora: kb. ' + myDog.dogAge))
],
)
],
),
),
);
}
I tried making this function async as well, and making it wait for the fetchBasicDetails() to finish, so the data is present when it would use it.
I even tried using dynamic fetchBasicData(String URL) async {...} but that didn't help either.
Using Future<Dog> instead of dynamic also causes errors.
How could I make the buildBasicWidget use the fetchBasicData result? Or should I just handle the widget building in the fetchBasicData as a simple workaround?
You need to use a FutureBuilder and add your async function in the future argument otherwise the build method gets called before the data are obtained.
Alternatively do your async request inside initState.

Resources