Flutter - Showing suggestion list on top of other widgets - dart

I am developing a screen where I have to show suggestions list below the textfield.
I want to achieve this
I have developed this so far
Following code shows textfield with suggestions in a list.
#override
Widget build(BuildContext context) {
final header = new Container(
height: 39.0,
padding: const EdgeInsets.only(left: 16.0, right: 2.0),
decoration: _textFieldBorderDecoration,
child: new Row(
children: <Widget>[
new Expanded(
child: new TextField(
maxLines: 1,
controller: _controller,
style: _textFieldTextStyle,
decoration:
const InputDecoration.collapsed(hintText: 'Enter location'),
onChanged: (v) {
_onTextChanged.add(v);
if (widget.onStartTyping != null) {
widget.onStartTyping();
}
},
),
),
new Container(
height: 32.0,
width: 32.0,
child: new InkWell(
child: new Icon(
Icons.clear,
size: 20.0,
color: const Color(0xFF7C7C7C),
),
borderRadius: new BorderRadius.circular(35.0),
onTap: (){
setState(() {
_controller.clear();
_places = [];
if (widget.onClearPressed != null) {
widget.onClearPressed();
}
});
},
),
),
],
),
);
if (_places.length > 0) {
final body = new Material(
elevation: 8.0,
child: new SingleChildScrollView(
child: new ListBody(
children: _places.map((p) {
return new InkWell(
child: new Container(
height: 38.0,
padding: const EdgeInsets.only(left: 16.0, right: 16.0),
alignment: Alignment.centerLeft,
decoration: _suggestionBorderDecoration,
child: new Text(
p.formattedAddress,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: _suggestionTextStyle,
),
),
onTap: () {
_getPlaceDetail(p);
},
);
}).toList(growable: false),
),
),
);
return new Container(
child: new Column(
children: <Widget>[header, body],
),
);
} else {
return new Container(
child: new Column(
children: <Widget>[header],
),
);
}
}
Header(Textfield) and body(Suggestions List - SingleChildScrollView with ListBody) is wrapped inside the Column widget, and column expands based on the total height of the children.
Now the problem is as Column expands, layout system pushes other widgets on screen to the bottom. But I want other widgets to stay on their positions but suggestion list starts to appear on top of other widgets.
How can I show suggestions list on top of other widgets? And the suggestions list is dynamic, as user types I call the Google Places API and update the suggestions list.
I have seen there is showMenu<T>() method with RelativeRect positions but it doesn't fulfills my purpose, my suggestion list is dynamic(changing based on user input) and the styling for each item I have is different from what PopupMenuItem provides.
There is one possibility I can think of using Stack widget as root widget of this screen and arrange everything by absolute position and I put suggestion list as a last child of the stack children list. But it is not the right solution I believe.
What other possibilities I need to look into? What other Widgets can be used here in this use-case?
And again use-case is simple, overlaying suggestion list on other widgets on the screen and when user tap any of the item from the list then hiding this overlaid suggestion list.

The reason why your autocomplete list pushes down the widgets below it is because the List is being expanded on the Container. You can use Flutter's Autocomplete widget and it should inflate the autocomplete list over other widgets.
var fruits = ['Apple', 'Banana', 'Mango', 'Orange'];
_autoCompleteTextField() {
return Autocomplete(
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return const Iterable<String>.empty();
}
return fruits.where((String option) {
return option
.toLowerCase()
.contains(textEditingValue.text.toLowerCase());
});
},
onSelected: (String selection) {
debugPrint('You just selected $selection');
},
);
}

Related

How do I prevent the infinite rendering of message tiles despite the number of chat rooms there are?

To whom it may concern,
I am working on a project in which I am required to work on functionality for chat rooms. I have the bulk of the code made and most of it runs just fine, but I am having issues rendering the user message tiles to the chats scroll list, or the tile the user clicks to enter a specific chat room. My problem is that I am trying to render only as many chat rooms as there are in the database for the user, and right now there is only 1 in the database, meaning only 1 message tile should be rendered. However, when the message tile is rendered, it is just rendered infinitely down the list, over and over. Even after adding a second user to the Added Users collection in Cloud Firestore, only the first user is ever rendered, and infinitely at that. How can I make it to where there are only as many message tiles as there are added users in the database and each user in the Added Users collection is only rendered once? I need this issue fixed in order to complete the functionality of chatting in my application, since the person the user chats with is dependent on which message tile they tap.
Please note that I am using Android Studio, Flutter/Dart, Firebase Authentication (though this is not very relevant in this case), and Firebase Cloud Firestore.
To be clear, I expect to see one message tile per Added Users in the Cloud Firestore database, and to have the ability to scroll through this list of users if there are more than the screen can hold. So far, I have tried debugging to see the issue. However, I believe that the infinite render issue is an issue with the logic of my code, which is why I am posting on Stack Overflow.
Below are pictures of the chats scroll page itself and the Cloud Firestore database for this project. Below these pictures is the code responsible for the chats scroll list page:
Pictures:
The chats scroll list. Message tiles can be seen here, being infinitely rendered
Picture of the Cloud Firestore database for this project. Note that Added Users is a subcollection of a user document within the collection Users
Code:
class ChatsPage extends StatelessWidget{
const ChatsPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context){
UserObject testReceiver = UserObject("test2#gmail.com", "MyTestUser2", "Test", "2", "456", "Test", "Test", "Test", "Test", 123456, "https://imgv3.fotor.com/images/blog-cover-image/10-profile-picture-ideas-to-make-you-stand-out.jpg");
// Create a custom scroll view to list user chats.
return CustomScrollView(
// Using slivers...
slivers: [
// Create a sliver app bar.
SliverAppBar(
// Set the color to whatever is necessary.
backgroundColor: Colors.black,
// Center the title text.
centerTitle: true,
// Set the title text.
title: Text("Chats Page"),
// Create an IconBackground object to display the back arrow icon.
leading: IconBackground(
// Set the icon itself to the back arrow icon.
icon: Icons.arrow_back,
// Upon pressing the icon...
onTap: (){
// Simply return to the previous screen.
Navigator.pop(context);
}),
actions: [
Padding(
padding: const EdgeInsets.only(right: 10.0),
// Add chat functionality should be implemented here.
child: IconBackground( icon: Icons.add, onTap: (){ print("Must complete add chat functionality"); })
),
Padding(
padding: const EdgeInsets.only(right: 15.0),
child: IconBackground(icon: Icons.search, onTap: (){ print("Must complete search functionality"); })
)
],
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection("Users").doc(FirebaseAuth.instance.currentUser?.uid.toString()).collection("Added Users").snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
if(snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}
return SizedBox(
height: 75,
child: ListView(
physics: const NeverScrollableScrollPhysics(),
children: snapshot.data!.docs.map((DocumentSnapshot document){
UserObject docUser = UserObject(
document.get("email"),
document.get("userName"),
document.get("firstName"),
document.get("lastName"),
document.get("userID"),
document.get("birthday"),
document.get("university"),
document.get("city"),
document.get("state"),
document.get("zipCode"),
document.get("profilePictureURL")
);
return _MessageChatTile(user: docUser);
}).toList(),
),
);
},
);
}
),
)
],
);
}
}
class _MessageChatTile extends StatelessWidget{
const _MessageChatTile({
Key? key,
required this.user
}) : super(key: key);
final UserObject user;
// There are 19 children in this one function.
#override
Widget build(BuildContext context){
// log("Hello? Does this work?");
return Material(
color: Colors.black,
child: InkWell(
onTap: () {
Navigator.of(context).push(ChatRoom.routeMessage(user));
},
child: Row(
children: [
Padding(
padding: const EdgeInsets.all(11.0),
child: Avatar.medium(url: user.profilePictureURL),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DefaultTextStyle(
style: const TextStyle(
fontSize: 20,
color: Colors.white,
letterSpacing: 0.2,
wordSpacing: 1.5
// fontWeight: FontWeight.w900
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(user.userName, overflow: TextOverflow.ellipsis),
// TODO: Add functionality for seeing messages outside of chat room.
//Text(messageData.message, overflow: TextOverflow.ellipsis)
],
),
)
]
),
),
Padding(
padding: const EdgeInsets.only(right: 20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DefaultTextStyle(
style: const TextStyle(
fontSize: 11,
letterSpacing: -0.2,
fontWeight: FontWeight.w600
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
const SizedBox(
height: 4,
),
// TODO: Add functionality for seeing message dates outside of chat room.
// Text(
// messageData.dateMessage.toUpperCase(),
// style: const TextStyle(
// fontSize: 11,
// letterSpacing: -0.2,
// fontWeight: FontWeight.w600,
// color: Colors.blueGrey
// ),
// ),
const SizedBox(
height: 8,
),
Container(
width: 18,
height: 18,
decoration: const BoxDecoration(
color: Colors.blueAccent,
shape: BoxShape.circle
),
child: const Center(
child: Text(
'1',
style: TextStyle(
fontSize: 10,
color: Colors.white
),
),
),
)
],
)
)
],
),
)
],
),
)
);
}
}

How to have four of the same custom widget have text change individually?

I am looking to create a grid with 4 custom widgets that can either add or subtract from a given starting number. See image for reference.
For example, if you press player one, the number would increase or decrease to 100 or 99. But the other 3 players would remain the same.
I had originally used one stateful widget with a separate function for each player, but I am sure there's a way to do it in a more modular way.
class CommanderDamage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return CommanderDamageState();
}
}
class CommanderDamageState extends State<CommanderDamage> {
int damage = 0;
void update() {
setState(() {
damage++;
});
}
#override
Widget build(context) {
return MaterialApp(
home: Scaffold(
body: GridView.builder(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemCount: 4,
itemBuilder: (BuildContext context, index) {
return Container(
child: Column(
children: <Widget>[
Text("Player " + index.toString()),
InkWell(
onTap: update,
child: Container(
width: 100.0,
height: 100.0,
child: Text(damage),
)
],
),
);
},
),
),
);
}
}
EDIT: I have edited my code to reflect my current. Currently, when the damage area is pressed, the damage increases for all 4 players instead of the one I am pressing.
Wrap your text widget inside InkWell(). Basically what InkWell does is creates a rectangular touch responsive area.
InkWell(
child: Text(
'Player One',
style: TextStyle(
fontSize: 20, color: Colors.white),
onTap: () {
// Your function
}
)
But this make the interactive tap area according to size of the text which is very small, so it's better to wrap it inside a container and provide height-width or some space with padding
InkWell(
child: Container(
width: 100,
height: 100,
child: Text(
'Player One',
style: TextStyle(
fontSize: 20, color: Colors.white), ),
onTap: () {
// Your function
}
)
An inside onTap you can your function and perform changes.
Read more about InkWell:
https://docs.flutter.io/flutter/material/InkWell-class.html
After lots of trial and error I managed to find an answer.
I had to set the state within the onTap instead of making a separate function and calling it in the onTap.
class CommanderDamage extends StatefulWidget {
int damage = 0;
CommanderDamage({this.damage, Key key});
#override
State<StatefulWidget> createState() {
return CommanderDamageState();
}
}
class CommanderDamageState extends State<CommanderDamage> {
var damage = [0, 0, 0, 0, 0, 0];
#override
Widget build(context) {
return MaterialApp(
home: Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft, end: Alignment.bottomRight,
colors: [Color(0xfff6921e), Color(0xffee4036)],
),
),
child: GridView.builder(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemCount: damage.length,
itemBuilder: (BuildContext context, index) {
return Container(
child: Column(
children: <Widget>[
InkWell(
onTap: () {
setState(() {
damage[index]++;
});
},
onLongPress: () {
setState(() {
damage[index] = 0;
});
},
child: Container(
width: 100.0,
height: 100.0,
child: Text(damage[index].toString()),
),
),
],
),
);
},
),
),
],
),
),
);
}
}

How expand text and container according text size?

I'm trying to create a card with a text within a container but I would like to show only a part of the text and when the user click on "show more", show the rest. I saw a Widget to construct text like this here, but I need expand the card container either and I don't know how to do that because I need to know how many lines the text have to expand with the correctly size. Exists a way to calculate the size according the number of lines or characters?
I tried to create the card as follows, where the DescriptionText is the Widget on the link and specify a minHeight in the Container in the hope of expanding the container along with the text but did not work.
Widget _showAnswerCard(Answer answer, User user) {
return Card(
elevation: 3.0,
color: Theme.of(context).backgroundColor,
child: Container(
constraints: BoxConstraints(minHeight: 90),
padding: EdgeInsets.all(10.0),
child: Flex(
direction: Axis.horizontal,
children: <Widget>[
Expanded(flex: 1, child: _showUserAvatar(answer)),
Expanded(flex: 3, child: _showAnswerDetails(answer, user)),
],
),
));
}
Widget _showAnswerDetails(Answer answer, User user) {
return Flex(
direction: Axis.vertical,
children: <Widget>[
Expanded(
flex: 3,
child: DescriptionTextWidget(text: answer.content),
),
Expanded(
flex: 1,
child: _showAnswerOptions(),
)
],
);
}
I'll really appreciate if someone could help me with that.
Just use Wrap widget to wrap your Card widget.
Based on your link for suggested answer. I did change to use Wrap widget.
Jus do copy/paste below code and check.
import 'package:flutter/material.dart';
class ProductDetailPage extends StatelessWidget {
final String description =
"Flutter is Google’s mobile UI framework for crafting high-quality native interfaces on iOS and Android in record time. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source.";
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: const Text("Demo App"),
),
body: new Container(
child: new DescriptionTextWidget(text: description),
),
);
}
}
class DescriptionTextWidget extends StatefulWidget {
final String text;
DescriptionTextWidget({#required this.text});
#override
_DescriptionTextWidgetState createState() =>
new _DescriptionTextWidgetState();
}
class _DescriptionTextWidgetState extends State<DescriptionTextWidget> {
bool flag = true;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Wrap(
children: <Widget>[
Card(
margin: EdgeInsets.all(8),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
child: Column(
children: <Widget>[
Container(
child: Text(
widget.text,
overflow: flag ? TextOverflow.ellipsis : null,
style: TextStyle(
fontSize: 15,
),
),
),
InkWell(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
flag ? "show more" : "show less",
style: new TextStyle(color: Colors.blue),
),
],
),
onTap: () {
setState(() {
flag = !flag;
});
},
),
],
)),
),
],
);
}
}
Result:
The solution I can think of is to use two labels, one for displaying only one line of text and one for displaying all the text. When the button is clicked, the two labels are alternately displayed in an animated manner. There is no computer at the moment, it is not convenient to verify, I hope to give you some help in the implementation of the program.

onTap doesn't work on ListWheelScrollView children items - Flutter

I'm trying to make a list of items using ListWheelScrollView and I want to have the ability of tapping on items but it seems onTap doesn't work.
Here is a simple code
List<int> numbers = [
1,
2,
3,
4,
5
];
...
Container(
height: 200,
child: ListWheelScrollView(
controller: fixedExtentScrollController,
physics: FixedExtentScrollPhysics(),
children: numbers.map((month) {
return Card(
child: GestureDetector(
onTap: () {
print(123);
},
child: Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
month.toString(),
style: TextStyle(fontSize: 18.0),
),
)),
],
),
));
}).toList(),
itemExtent: 60.0,
),
)
Is there something wrong with this code ? I'm pretty sure something like this will work on a ListView or other scrolling widgets.
I solved the problem. I hope is helpful.
create a int variable in State class.
class _MenuWheelState extends State {
int _vIndiceWheel;
in the Function onSelectedItemChanged of the ListWheelScrollView set the variable:
onSelectedItemChanged: (ValueChanged) {
setState(() {
_vIndiceWheel = ValueChanged; });
},
create a GestureDetecture and put the ListWheelScrollView inside:
GestureDetector(
child: ListWheelScrollView(...
create onTap function at the GestureDetecture like this code:
// this is necessary
if (_vIndiceWheel == null) {
_vIndiceWheel = 0;
}
switch (_vIndiceWheel) {
case 0:
{
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return YourSecondScreen();
},
...

Creating a Sticky Site Footer

I have not been able to locate any documentation for creating footer nav bars with Flutter/Dart. I know that "crossAxisAlignment: CrossAxisAlignment.end" can be used to pull content to the bottom of a column. However, I'm not sure how to render a site footer that sticks to the bottom of the screen. There are various solutions in flex and css grid, but not clear on what implementation would look like in this platform.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
Widget siteLogo = new Container(
padding: const EdgeInsets.only(top: 100.0),
child: new Image.asset(
'images/logo.png',
width: 180.0,
height: 180.0,
),
);
Widget titleTextSection = new Container(
padding: const EdgeInsets.only(left: 80.0, right: 80.0, top: 30.0, bottom: 20.0),
child: new Text(
''' Welcome to The Site''',
textAlign: TextAlign.center,
style: new TextStyle(
fontSize: 35.0,
fontWeight: FontWeight.w500,
),
),
);
Widget subtitleTextSection = new Container(
padding: const EdgeInsets.only(left: 40.0, right: 40.0, bottom: 40.0),
child: new Text(
'''Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec imperdiet Donec imperdiet.''',
textAlign: TextAlign.center,
style: new TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.w400
),
),
);
// footer
Column signInLink(String label) {
return new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
new Container(
child: new Text(
label,
style: new TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w400,
color: const Color(0xFFf735e9)
),
),
),
],
);
}
Column existingAccountLink(String label) {
return new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
new Container(
child: new Text(
label,
style: new TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w400,
color: const Color(0xFFeceff1)
),
),
),
],
);
}
Widget footer = new Container(
height: 50.0,
decoration: new BoxDecoration(color: Colors.black),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
existingAccountLink('Already have an account?'),
signInLink('SIGN IN'),
],
),
);
return new MaterialApp(
title: 'Flutter Demo',
home: new Scaffold(
body: new Container(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage('images/backgroundimg.jpg'),
fit: BoxFit.cover,
),
),
child: new ListView(
children: [
siteLogo,
titleTextSection,
subtitleTextSection,
footer,
],
),
)
),
);
}
}
As you can see, I am currently using ListView to layout the widgets. The only thing I can think of right now is to put the whole ListView in a single column, crossAxisAlign to End so everything gets pulled down, and then style each widget relative to the footer. There must be a better way though?
Since you are using Scaffold, you can use bottomNavigationBar to have a 'sticky' bottom widget. (And potentially use BottomNavigationBar if you want to)
new Scaffold(
appBar: new AppBar(title: new Text("Title")),
body: new ListView.builder(
itemBuilder: (context, index) {
return new ListTile(
title: new Text("title $index"),
);
},
),
bottomNavigationBar: new Container(
height: 40.0,
color: Colors.red,
),
);
Alternatively your
The only thing I can think of right now is to put the whole ListView in a single column
is not a bad idea at all. That's how things works in flutter.
Although MainAxisAlignement.end is not the right way to do it.
You could achieve the same layout as with bottomNavigationBar without a Scaffold, using a Column this way :
new Column(
children: <Widget>[
new Expanded(
child: new ListView.builder(
itemBuilder: (context, index) {
return new ListTile(
title: new Text("title $index"),
);
},
),
),
new Container(
height: 40.0,
color: Colors.red,
),
],
),
The use of a listview is an excellent choice when you have a list of items that need a listview. Sometimes the other items in the layout might be fixed and do not need a listview. e.g using a column.
Example on how to achieve fixed bottom footer using stack.
#override
Widget build(BuildContext context) {
return new Material(
child: Scaffold(
body: Stack(
children : <Widget> [
Text("Top positioned text"),
Column(
children : <Widget> [
Text("Column:Top positioned text"),
Text("Column:Top positioned text")
]
),
Positioned(
bottom : 0,
child: Text("Bottom positioned text")
)
]
)
}
you can use Footer from flutter_layouts
sticky footer can be hard to implement. since you have to know the height of footer, it is not possible to perform this in build() function.
try out below flutter package.
https://github.com/softmarshmallow/flutter-layouts/tree/master/lib/src/footer
https://github.com/softmarshmallow/flutter-layouts/
by this, you can use footer with scaffold bottom nav. example
import 'package:flutter_layouts/flutter_layouts.dart';
class _FooterScreenState extends State<FooterScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("footer demo"),
),
body: buildBody(),
bottomNavigationBar: BottomNavigationBar(items: [
BottomNavigationBarItem(icon: Icon(Icons.add), title: Text("first")),
BottomNavigationBarItem(icon: Icon(Icons.remove), title: Text("second"))
]),
);
}
Widget buildBody() {
return Footer(
body: buildContent(),
footer: buildFooter(),
);
}
Widget buildContent() {
return ListView.builder(
itemBuilder: (c, i) {
return Card(
margin: EdgeInsets.all(16),
child: Container(
padding: EdgeInsets.all(24),
child: Text("contents"),
),
);
},
itemCount: 20,
);
}
Widget buildFooter() {
return Container(
padding: EdgeInsets.all(24),
decoration: BoxDecoration(color: Theme.of(context).primaryColor),
child: FlatButton(
onPressed: () {},
child: Text("Lean more", style: Theme.of(context).textTheme.button.copyWith(
color: Theme.of(context).colorScheme.onBackground
),),
),
);
}
}

Resources