Using TextField and Button to create a message bubble in Flutter - flutter

Android Studio + Flutter
I have a Row at the bottom of the screen.
It includes TextField and Button (Add).
When there is some text in TextField and user clicks Add, I want it to appear as a Bubble inside a Container starting from the top left.
What would be the correct way to do it? I want the bubbles to accumulate like a notes app and eventually be scrollable too.
Thanks!

Try using the Snackbar Widget, which can be personalized heavily.
Here's the documentation.
EDIT. Since you want a permanent list of bubbles on the top, I'd suggest using a provider, so that when you click the button, the onTap event validates and adds the data to a list (below, myElements). Then, up to the top, just add a Consumer Widget that listens to changes to the list (it rebuilds its children every time something changes). In the following example code (I have not tested it!) I use an Expanded widget just for fun and I use a ListView.builder inside the Consumer to show the list of elements you've added, since the amount of added element could be high. Finally, I suggest using either ListTile or Card or a combination of the two, since you want something aesthetically beatiful like a bubble (you'll have to play with the settings, a little):
return ... (
child: Column(
children: [
Text("Some title..?"),
Expanded(
Consumer(
builder: (ctx, myElements, _) => ListView.builder(
itemCount: myElements.length,
itemBuilder: (ctx, i) => ListTile(
title: Text("You added ${myElements[i]}"),
// something else...?
),
),
),
),
Row( /* ... Text field + Button here... */),
],
),
// ...

Related

Flutter GridView: how to don't generate items when GridView is not primary

I try to make an apps using long GridView with complexe item. I use GridView.builder which is optimize and it creates visible items (and it do the job !).
But in my case, I need some widget before and I must add Column() and SingleChildScrollView.
When I do that I need to change GridView.builder with primary=false and shrinkWrap: true.
But now, all GridView items are generated.
EDIT: New demo
My wanted behavior is the mode "ColumnWithGrid".
Check this demo to understand issue.
Press top buttons to switch modes: open Console and check log
https://dartpad.dev/?id=4f60ffbf656767a6e5c5bccc280acd3a
I think "shrinkWrap" property must stay to false but I never success to keep it in this case.
My question:
How to use GridView.builder properly when I need to include it inside Column() or whatever ?
How to make the mode "ColumnWithGrid" without generate full list (using dev.pub, ...) ?
Thanks
After some search and experiment, I found some posts about this topic (which is true for GridView or ListView widget) and my conclusion is :
GridView doesn't work like I expect !
When I create just a single GridView, it's like I create a container of my full device area and I put GridView inside.
This "hidden container" just keep info visible inside this container area.
So if I include my GridView inside Column without any container, it doesn't create it for me and unroll all my data to compute properly size.
The feature that expected is : GridView computes only items at screen and unroll virtually data (so manage local/global slider position to create only item inside visible area).
I update my demo to show the effect about all cases.
Sources:
https://docs.flutter.dev/cookbook/lists/long-lists
https://medium.com/saugo360/flutter-creating-a-listview-that-loads-one-page-at-a-time-c5c91b6fabd3
A CustomScrollView in combination with SliverList and SliverGrid can be used to achieve lazy loading.
CustomScrollView(slivers: [
SliverList(
delegate: SliverChildListDelegate([
const Center(
child: Text(
"Header",
style: TextStyle(fontSize: 40),
),
),
]),
),
SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
print("generate the item $index");
return Container(color: Colors.blue);
},
childCount: 100,
),
),
]),
https://dartpad.dev/?id=9633305d9a2daa0905de852fa003aba1

SliverList setState to Affect Only Selected Item Flutter

I have a button on every item on my SliverList. When I click a specific list item button, I wish it change to a different widget by using setState. Only that specific item button should change to a different widget while the rest on the list retains its own item button.
Example below. The problem of course is that when I click any button on the list, all buttons on every item on the list changes. What is needed so that it affects only the specific item on the list whose button was pressed?
SliverList(
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
final item = gd[index];
return Container(
child: Column(
children: [
item.mypicwidget(context,item.pid),
_shownewwidget
? item.newwidget(context)
: RaisedButton(
onPressed: () {
setState(() {
_shownewwidget=true;
});
},
child: const Text('Press Me'),
),
]
),
);
Figured this out.
I believe the approach is to add to the existing list an object key to represent whether widget1 or widget2 to render, eg. tag: 'normal' or tag: 'clicked'. Depending on index.tag, either widget1 or widget2 will be displayed. So for a widget with a button wherein the tag is normal, the button will have a function when clicked changes the index.tag of the item from normal to clicked. When the Sliverlist sees this new value, it automatically renders the corresponding widget.

Flutter - SliverAppBar with snapping toolbar and nested scroll view

I'm trying to reproduce a UX design in which we have the following components:
A top bar which contains the text "Desmos"
A main post content, which is composed of
A header (user icon, user name, post date, options button)
The post message
Any post image that might be present
A tab bar, which allows the user to show either the Comments list or the Reactions list.
The peculiarity of this UX is that when the user scrools the list of Comments or Reactions (based on which he is currently viewing), the vertical scroll list snaps to the tab bar.
From there, the user will be able to either scroll down the list of comments or, with an additional scroll, showing again the post. Please note that the original post should be placed so that for the user to show it again, a higher amount of scroll force is required.
I have put together a video that you can see here to better understand how this should work:
I've already realized a widget called PostContent that contains all the above mentioned main content of a post. I've also already tried coding something for the wanted UX that looks like the following:
Scaffold(
body: SafeArea(
child: NestedScrollView(
headerSliverBuilder: (context, _) {
return [
SliverAppBar(
backgroundColor: Colors.white,
expandedHeight: 310,
pinned: true,
primary: true,
flexibleSpace: PostContent(post: post),
)
];
},
body: ListView.builder(
itemBuilder: (context, index) {
return Text(index.toString());
},
),
),
),
);
The result can be seen by clicking on the following preview:
The problems with this implementation are the following:
As the PostContent is a Column, how can I get its height so that I don't have to specify a fixed value inside SliverAppBar's expandedHeight attribute?
How can I avoid that when scrolling up the ListView, the scrolling stops when it reaches the AppBar without scrolling further up?

How to dynamically add Children to Scaffold Widget

Let's say, I have a chat screen that looks like this.
Now, when the user clicks the "Press when ready" button, the method fetchNewQuestion() is called.
My intention is that this will make a HTTP request, and display the result using
_buildUsersReply(httpResponse);
But, the problem is that this return must be made inside the current scaffold's widget as a child under the existing children, so that it is built at the bottom with the previous ones still there. The result would be like this:
You can find my complete code here.
Is this possible to be done pro-grammatically? Or do I have to change the concept of how I do this?
[Update, I now understand that my approach above is wrong and I have to use a listview builder. CurrentStatus below shows my progress towards achieving that goal.]
Current status:
I have built a list of Widgets:
List<Widget> chatScreenWidgets = [];
And on setState, I am updating that with a new Widget using this:
setState(() { chatScreenWidgets.add(_buildUsersReply("I think there were 35 humans and one horse.")); });
Now at this point, I am not sure how to pass the widget inside the scaffold. I have written some code that does not work. For instance, I tried this:
Code in the image below and in the gist here:
Just for future reference, here is what I really needed to do:
1. Create a list of widgets
List<Widget> chatScreenWidgets = [];
2. Inside my method, I needed to use a setState in order to add elements to that list. Every widget I add to this will be displayed on ths Scaffold.
`setState(() {
chatScreenWidgets.add(_buildUsersReply("Some Text"));
});`
3. And then, load that inside my Scaffold, I used an itemBuilder in order to return a list of widgets to my ListView. I already had that ListView (where I was manually adding children). Now this just returns them through the setState method inside my business logic method (in this case, fetchNewQuestion()).
body: Stack(
children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 0),
child: new ListView.builder(
physics: BouncingScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: 25),
itemCount: chatScreenWidgets.length,
itemBuilder: (BuildContext context, int itemCount) {
return chatScreenWidgets[itemCount];
}
),
),
],
),
);`
I hope this helps future flutter engineers!
forget about the scaffold the idea is about what you really want to change, lets say it is
a list and your getting the data from an array if you update the array, then the list will update,if it is another type widgets then you can handle it in a different way i will edit this answer if you clarify what each part does in your widget as i cant read your full code.
first you have to create an object with two attributes one is the type of the row(if it is a user replay or other messages) and the second attribute is the string or the text.
now create a global list in the listview class from the above object, so you get the data from the user or even as a news and you create a new object from the above class and add your data to it and add it to the list.
item builder returns a widget so according to the the widget that you return the row will be set , so according to the data in the object call one of your functions that return the views like _buildUsersReply(string text).
if you have any other question you can ask :) if this what you need please mark it as the answer.

Why Flutter ListTile onTap seems cached?

I have two listview screen on my app. User can navigate between them using BottomNavigationBar control.
On listview.builder function, I return something like this
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('...'),
subtitle: Text('...'),
onTap: () {
...
}
)
});
I found onTap handler seems mixed between those 2 listviews.
When I open first list view, flutter serve the correct onTap,but when I switch to second listview, flutter still serving the first listview onTap.
Seems the onTap is cached by flutter (title & subtitle seems okay). Any idea?
Sample source code: https://github.com/jazarja/flutter_app
The problem is in your compararing transition values. Because you are first reversing the animation transition of current view.
In your botton_nav.dart, change this:
return aValue.compareTo(bValue);
to this:
return bValue.compareTo(aValue);
Yes the problem is that the animations haven't completed by the time the comparison is being done so the widget at the top of the stack is always 1 tab selection behind. You actually don't need to build a stack at all, just replace Center(child: _buildTransitionStack()) here with _navigationViews[_currentIndex].transition(_type, context) and it should work.