Open Drawer of current (topmost) route's Scaffold - flutter

I'm writing an audio player. Like most media players (Youtube, Spotify, etc), I want a "remote" overlay on the screen while media is playing. No matter what the user is doing, they should be able to control the media.
I accomplished that with a Stack under MaterialApp
MaterialApp(
title: 'MyApp',
navigatorObservers: [gRouteObserver],
routes: appRoutes,
builder: (context, child) {
return Stack(children: [
child!,
Positioned(
bottom: 55,
width: MediaQuery.of(context).size.width,
child: StatefulBuilder(builder: (context, _setState) {
gPlayer.widgetSBRefresher = _setState;
return gPlayer.started ? gPlayer.widget : const SizedBox(height: 0);
}))
]);
});
gPlayer.widget references this
class MiniPlayer extends StatefulWidget {
const MiniPlayer({Key? key}) : super(key: key);
#override
State<MiniPlayer> createState() => MiniPlayerState();
}
class MiniPlayerState extends State<MiniPlayer> with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
#override
Widget build(context) {
super.build(context);
return Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
color: Colors.black,
child: Padding(
padding: const EdgeInsets.all(2.0),
child: Column(
children: [
Material(
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AvatarAlone(id: gPlayer.current!.owner),
Expanded(
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Text(gPlayer.playing
? "Now Playing"
: "Paused"),
),
),
// here is the code I'll
// be talking about -->
IconButton(
color: Colors.white,
iconSize: 20,
icon: const Icon(MyIcons.bookmark),
onPressed: gPlayer.bookmarkBuilder,
),
InkWell(child: Icon(gPlayer.playing ? MyIcons.pauseCircle : MyIcons.playCircle, size: 50), onTap: gPlayer.playPause)
],
),
),
],
),
),
),
));
}
refresh() {
setState(() {});
}
}
I used a code comment to point out this icon button.
IconButton(
color: Colors.white,
iconSize: 20,
icon: const Icon(MyIcons.bookmark),
onPressed: gPlayer.bookmarkBuilder,
),
So, when this widget is open and the app is on the home route ("/"), I can do
bookmarkBuilder() {
Scaffold.of(gScaffApp.currentContext!).openDrawer();
}
and it will open the drawer.
I've attached the same drawers to all my routes' scaffolds.
When other routes are up, with their own scaffolds, I want bookmarkBuilder to open the drawer on the topmost route. But I can't quite figure out how.

So I have a working solution to this, but I don't love it.
I created a global variable, gScaffs, with gScaffApp as the first element.
List<GlobalKey<ScaffoldState>> gScaffs = [gScaffApp];
My secondary routes all use the same base scaffold widget
class _CardScaffoldState extends State<CardScaffold> {
#override
initState() {
super.initState();
gScaffs.add(GlobalKey());
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(key: gScaffs.last,
drawer: DrawerBookmarks()
...
And the dispose method looks like this.
#override
dispose() {
super.dispose();
gScaffs.removeLast();
}
And then, in my bookmarkBuilder function, I have this.
It's not clear to me why, but gScaffApp needs the drawer triggered one way, while the CardScaffolds need the drawer triggered the other way.
bookmarkBuilder() {
if (gScaffApp == gScaffs.last) {
Scaffold.of(gScaffApp.currentContext!).openDrawer();
} else {
gScaffs.last.currentState!.openDrawer();
}
}

Related

Flutter DefaultTabController add/edit a button when the user reach last tab

I have got a DefaultTabController with a couple of tabs that the user can swipe or press an ElevatedButton to proceed to the next slide, my issue is that I don't know how to change the button's label when the user reaches the last tab using swipes.
Using a stateful widget I managed to change the label when the user presses the button but it doesn't work if the user swipes. Is it possible to change the button when the user reaches the last tab?
class SlidesWidget extends StatelessWidget {
static List<Slide> slides = [
const Slide(
text: 'Welcome to ..'),
const Slide(
text: 'Ready to discover your city?')
];
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: slides.length,
child: Builder( // Builder here, otherwise `DefaultTabController.of(context)` returns null.
builder: (BuildContext context) => Padding(
padding: const EdgeInsets.all(8.0),
child: SafeArea(
child: Column(
children: [
const TabPageSelector(
selectedColor: Colors.white,
),
Expanded(
flex: 100,
child: TabBarView(
children: slides,
),
),
Padding(
padding: const EdgeInsets.all(18.0),
child: ElevatedButton(
onPressed: () {
final TabController controller =
DefaultTabController.of(context)!;
if (!controller.indexIsChanging &&
controller.index < slides.length - 1) {
// Go to next slide if exists
controller.index++;
}
},
child: Text('Next'), // <== on last slide should change label and do other things
),
)
],
),
),
),
),
);
}
}
I will recommend using StatefulWidget, also you can use inline StatefulBuilder to update the UI. And using TabController is handy instead of calling it multiple times, and there is risk of getting null for DefaultTabController.
class SlidesWidget extends StatefulWidget {
SlidesWidget({Key? key}) : super(key: key);
#override
State<SlidesWidget> createState() => _SlidesWidgetState();
}
class _SlidesWidgetState extends State<SlidesWidget>
with SingleTickerProviderStateMixin {
late TabController controller;
List<Slide> slides = [
const Slide(text: 'Welcome to ..'),
const Slide(text: 'Ready to discover your city?')
];
#override
void initState() {
super.initState();
controller = TabController(length: slides.length, vsync: this)
..addListener(() {
setState(() {});
});
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(8.0),
child: SafeArea(
child: Column(
children: [
TabPageSelector(
controller: controller,
selectedColor: Colors.white,
),
Expanded(
flex: 100,
child: TabBarView(
controller: controller,
children: slides,
),
),
Padding(
padding: const EdgeInsets.all(18.0),
child: ElevatedButton(
onPressed: () {
if (!controller.indexIsChanging &&
controller.index < slides.length - 1) {
// Go to next slide if exists
controller.index++;
}
},
child: Text(controller.index == 1 ? 'start' : "Next"), //
),
)
],
),
),
));
}
}
More about TabController and I think you will also like IndexedStack for this case.

In Flutter, what's the easiest, simplest way to make a container flashing once without involving animation widgets?

Imagine Facebook mobile app, where you tap on the notification about someone like your comment. The app will open the appropriate screen, scroll you down to the comment, and after you arrive there, the comment row will flash yellow for a while, rapidly turn transparent, and then it's done.
I just want to make the same flashing animation to a ListView/Column element to let users know that something is happening there as a result of their action. But from what I gathered, to create just a simple animation like that needs a complex elaborate contraption with Animation widgets.
There's a widget that does a much appreciated fade animation called FadeInImage. I just need to provide destination URL, placeholder image asset, and the widget will handle the rest. I'm wondering if there's such alternative where I can just provide a key to a widget, and then call from anywhere: rowKey.currentState.flash(color: Colors.yellow). Or perhaps a way to let me tell the ListView or Column to flash certain row like listViewKey.currentState.items[5].flash(color: Colors.yellow).
There is no a Widget like you are looking for, but you can create a custom widget if you know the Flutter basics. You will be able to build from simple animations to the most advanced ones.
I made a simple example, a list of elements where you can select any element from the list (index).
When you open the screen, you will see the scroll animation, after that, the blink animation will start.
class FlashingHome extends StatelessWidget {
const FlashingHome({Key? key}) : super(key: key);
void _goToWidget(BuildContext context, int index) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => FlashingList(index: index),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
MaterialButton(
color: Colors.greenAccent,
child: const Text('Go to element 5'),
onPressed: () => _goToWidget(context, 5),
),
MaterialButton(
color: Colors.greenAccent,
child: const Text('Go to element 10'),
onPressed: () => _goToWidget(context, 10),
),
],
),
),
);
}
}
class FlashingList extends StatefulWidget {
const FlashingList({required this.index, Key? key}) : super(key: key);
final int index;
#override
State<FlashingList> createState() => _FlashingListState();
}
class _FlashingListState extends State<FlashingList>
with SingleTickerProviderStateMixin {
final _scrollController = ScrollController();
late final AnimationController _animationController;
final _itemSize = 150.0;
Timer? _timer;
Future<void> _startScrolling() async {
await _scrollController.animateTo(
_itemSize * widget.index,
duration: const Duration(seconds: 1),
curve: Curves.easeOut,
);
// after the scroll animation finishes, start the blinking
_animationController.repeat(reverse: true);
// the duration of the blinking
_timer = Timer(const Duration(seconds: 3), () {
setState(() {
_animationController.stop();
_timer?.cancel();
});
});
}
#override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
);
WidgetsBinding.instance!.addPostFrameCallback((_) => _startScrolling());
super.initState();
}
#override
void dispose() {
_timer?.cancel();
_animationController.dispose();
_scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flashing List'),
),
body: ListView.builder(
controller: _scrollController,
itemCount: 15,
itemExtent: 150,
itemBuilder: (context, index) {
final item = Padding(
padding: const EdgeInsets.all(20.0),
child: Text('My Item :$index'),
);
return Padding(
padding: const EdgeInsets.all(4.0),
child: FittedBox(
child: index == widget.index && _animationController.isDismissed
? FadeTransition(
opacity: _animationController,
child: Container(
color: Colors.yellow,
child: item,
),
)
: Container(
color: Colors.grey[200],
child: item,
),
),
);
},
),
);
}
}
Result:
Now that you know how to create an automatic scrolling list, animated item, you can customize this with a more complex animation and extract into a custom widget that you can reuse in your projects.
Reference: https://docs.flutter.dev/development/ui/animations
Try shimmer
While the data is being fetched, you can create a simple animation which will give the feel that something's loading. Here's a simple example.
I am using FAB onPress to reflect the changes.
bool isApiCallProcess = false;
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
isApiCallProcess = !isApiCallProcess;
});
},
),
backgroundColor: Colors.white,
body: (isApiCallProcess == false)
? CircleAvatar(
backgroundColor:
Colors.black12,
radius: 40,
backgroundImage: AssetImage(
'images/dosa.jpg',
),
):
Shimmer.fromColors(
baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!,
child: Wrap(
children: [
Column(
children: [
const CircleAvatar(
radius: 40,
backgroundImage: AssetImage(
'',
),
),
const SizedBox(
height: 10,
),
Container(
decoration: ShapeDecoration(
color: Colors.grey[400]!,
shape: const
RoundedRectangleBorder(),
),
height: 12,
width: 60,
),
],
),
],
),
),
),
);
Here's the screenshots :

TextField stays behind Keyboard when in Focus at BottomSheet on a real device but works on Emulator

I saw some solutions and implemented them. Look Ok on Emulator but doesn't work on Real Device (See screens). So when I click on a textField, the keyboard moves up as per in-focus Textfield and I am able to scroll, however does not happen in real device. Thanks in advance for your time.
BottomSheet launched at FloatinActionButton
showModalBottomSheet(
isScrollControlled: true,
backgroundColor: Colors.transparent,
context: context,
builder: (context) => SingleChildScrollView(
child: Container(
child: PostAd(),
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
)),
),
);
The method PostAD shows several pages in Container (depending on Index chosen by user)
return Center(
child: Container(
height: MediaQuery.of(context).size.height / 1.5,
child: Center(
child: Container(
child: Column(
children: [
Center(child: radioButtonCreate()), // Radio Buttons to select Page Index
Expanded(child: kadFormList[formIndex]), //Pages i.e forms that has text field
],
),
),
),
import 'package:flutter/material.dart';
import 'package:keyboard_visibility/keyboard_visibility.dart';
main() => runApp(MaterialApp(
home: Scaffold(
body: MyForm(),
),
));
class MyForm extends StatefulWidget {
MyForm() : super();
#override
State<StatefulWidget> createState() => MyFormState();
}
class MyFormState extends State<MyForm> {
bool isKeyboardVisible = false;
#override
void initState() {
super.initState();
KeyboardVisibilityNotification().addNewListener(
onChange: (bool visible) {
if (!visible) {
setState(() {
FocusScope.of(context).unfocus();
});
}
},
);
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(20.0, 20, 20, 0),
child: SingleChildScrollView(
reverse: isKeyboardVisible ? true : false,
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 100),
child: Column(
children: <Widget>[
for (int i=0; i<100; i++) TextField()
],
),
),
),
);
}
}

Listview scrolling and selecting Textfield afterwards is freezing my app

I am using the package
country_code_picker: ^1.4.0
https://pub.dev/packages/country_code_picker#-installing-tab-
with flutter 1.17.3
Which is pretty much one of the only country code picker packages. But I have one serious problem an I don't have a clue what it could be.
When I run this code
import 'package:flutter/material.dart';
import 'package:country_code_picker/country_code_picker.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
App();
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: TestWidget(),
);
}
}
class TestWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(body: _buildCountryPicker(context));
}
Widget _buildCountryPicker(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Center(
child: CountryCodePicker(
initialSelection: 'NL',
),
),
);
}
}
And I open the dialog to select a country. I scroll in the list and then select the TextField my keyboard opens and when I try to type something my entire app freezes. I can't even hot reload. I don't get a single error.
I am running this on my Huawei P30, but I also experience this on other android devices. I don't know if this is a flutter bug or a country code picker bug.
I think it is probably in this widget somewhere. If anyone could point me in the right direction it would help me alot!
class SelectionDialog extends StatefulWidget {
final List<CountryCode> elements;
final bool showCountryOnly;
final InputDecoration searchDecoration;
final TextStyle searchStyle;
final TextStyle textStyle;
final WidgetBuilder emptySearchBuilder;
final bool showFlag;
final double flagWidth;
final Size size;
final bool hideSearch;
/// elements passed as favorite
final List<CountryCode> favoriteElements;
SelectionDialog(
this.elements,
this.favoriteElements, {
Key key,
this.showCountryOnly,
this.emptySearchBuilder,
InputDecoration searchDecoration = const InputDecoration(),
this.searchStyle,
this.textStyle,
this.showFlag,
this.flagWidth = 32,
this.size,
this.hideSearch = false,
}) : assert(searchDecoration != null, 'searchDecoration must not be null!'),
this.searchDecoration =
searchDecoration.copyWith(prefixIcon: Icon(Icons.search)),
super(key: key);
#override
State<StatefulWidget> createState() => _SelectionDialogState();
}
class _SelectionDialogState extends State<SelectionDialog> {
/// this is useful for filtering purpose
List<CountryCode> filteredElements;
#override
Widget build(BuildContext context) => SimpleDialog(
titlePadding: const EdgeInsets.all(0),
title: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
IconButton(
padding: const EdgeInsets.all(0),
iconSize: 20,
icon: Icon(
Icons.close,
),
onPressed: () => Navigator.pop(context),
),
if (!widget.hideSearch)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextField(
style: widget.searchStyle,
decoration: widget.searchDecoration,
onChanged: _filterElements,
),
),
],
),
children: [
Container(
width: widget.size?.width ?? MediaQuery.of(context).size.width,
height:
widget.size?.height ?? MediaQuery.of(context).size.height * 0.7,
child: ListView(
children: [
widget.favoriteElements.isEmpty
? const DecoratedBox(decoration: BoxDecoration())
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...widget.favoriteElements.map(
(f) => SimpleDialogOption(
child: _buildOption(f),
onPressed: () {
_selectItem(f);
},
),
),
const Divider(),
],
),
if (filteredElements.isEmpty)
_buildEmptySearchWidget(context)
else
...filteredElements.map(
(e) => SimpleDialogOption(
key: Key(e.toLongString()),
child: _buildOption(e),
onPressed: () {
_selectItem(e);
},
),
),
],
),
),
],
);
Widget _buildOption(CountryCode e) {
return Container(
width: 400,
child: Flex(
direction: Axis.horizontal,
children: <Widget>[
if (widget.showFlag)
Flexible(
child: Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Image.asset(
e.flagUri,
package: 'country_code_picker',
width: widget.flagWidth,
),
),
),
Expanded(
flex: 4,
child: Text(
widget.showCountryOnly
? e.toCountryStringOnly()
: e.toLongString(),
overflow: TextOverflow.fade,
style: widget.textStyle,
),
),
],
),
);
}
Widget _buildEmptySearchWidget(BuildContext context) {
if (widget.emptySearchBuilder != null) {
return widget.emptySearchBuilder(context);
}
return Center(
child: Text('No country found'),
);
}
#override
void initState() {
filteredElements = widget.elements;
super.initState();
}
void _filterElements(String s) {
s = s.toUpperCase();
setState(() {
filteredElements = widget.elements
.where((e) =>
e.code.contains(s) ||
e.dialCode.contains(s) ||
e.name.toUpperCase().contains(s))
.toList();
});
}
void _selectItem(CountryCode e) {
Navigator.pop(context, e);
}
}
Also filed an issue on the flutter github https://github.com/flutter/flutter/issues/59886
Edit:
I have a video of it right here
https://www.youtube.com/watch?v=669KitFG9ek&feature=youtu.be
I just had to remove the keys, so there probably was a duplicate key
...filteredElements.map(
(e) => SimpleDialogOption(
//key: Key(e.toLongString()),
child: _buildOption(e),
onPressed: () {
_selectItem(e);
},
),
),

Can't make bottom textfield stick on top of keyboard when it show up in chat app, Flutter

So I m developing a chat app which read and write data from firebase.
I have a streambuilder(that shows the messages)which is above a Container widget(which hold the input text field)
My problem is when I tap the input field and the keyboard pop ups, it cover the message textfield.
I have done many solutions from Stackoverflow and none of them seems to work in my case. The technique i have tried are
-resizeToAvoidBottomInset: true
-Expanded(when I try this the messages no longer show up)
-Flexible
I test the same code in my other project and it works. The text field stick on top of the keyboard. It just doesn't work in a particular project which use Bloc Pattern. There might have been some scaffold error or I don't know. Please help
import 'chat_design.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
final _firestore = Firestore.instance;
FirebaseUser loggedInUser;
class ChatScreen extends StatefulWidget {
static const String id = 'chat_screen';
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final messageTextController = TextEditingController();
final _auth = FirebaseAuth.instance;
String messageText;
#override
void initState() {
// TODO: implement initState
super.initState();
getCurrentUser();
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser();
if (user != null) {
loggedInUser = user;
print(loggedInUser.email);
}
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: null,
actions: <Widget>[
IconButton(
icon: Icon(Icons.close),
onPressed: () {
_auth.signOut();
Navigator.pop(context);
}),
],
title: Text('⚡️Chat'),
backgroundColor: Colors.lightBlueAccent,
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
MessagesStream(),
Container(
decoration: kMessageContainerDecoration,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: messageTextController,
onChanged: (value) {
//Do something with the user input.
messageText = value;
},
decoration: kMessageTextFieldDecoration,
),
),
FlatButton(
onPressed: () {
messageTextController.clear();
//Implement send functionality.
_firestore.collection('messages').add({
'text': messageText,
'sender': loggedInUser.email,
});
},
child: Text(
'Send',
style: kSendButtonTextStyle,
),
),
],
),
),
],
),
),
);
}
}
class MessagesStream extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('messages').limit(100).snapshots(),
builder: (context, snapshot) {
//wait before data is loaded
if(snapshot.data == null) return Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(
width: 50,
height: 50,
child: CircularProgressIndicator(strokeWidth: 7,),
),
],
);
final messages = snapshot.data.documents.reversed;
List<MessageBubble> messageBubbles = [];
for (var message in messages) {
final messageText = message.data['text'];
final messageSender = message.data['sender'];
final currentUser = loggedInUser.email;
final messageBubble = MessageBubble(
sender: messageSender,
text: messageText,
isMe :currentUser == messageSender,
);
messageBubbles.add(messageBubble);
}
return Expanded(
child: ListView(
reverse: true,
padding:
EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
children: messageBubbles,
),
);
},
);
}
}
class MessageBubble extends StatelessWidget {
MessageBubble({this.sender, this.text,this.isMe});
final String sender;
final String text;
final bool isMe;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
Text(
sender,
style: TextStyle(
fontSize: 12.0,
color: Colors.black54,
),
),
Material(
borderRadius: isMe ? BorderRadius.only(topLeft: Radius.circular(30.0),
bottomLeft: Radius.circular(30.0),
bottomRight: Radius.circular(15.0))
:BorderRadius.only(topRight: Radius.circular(30.0),
bottomLeft: Radius.circular(15.0),
bottomRight: Radius.circular(30.0)),
color: isMe ? Colors.lightBlueAccent: Colors.white,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
child: Text(
'$text',
style: TextStyle(
color: isMe ? Colors.white : Colors.black,
fontSize: 15.0,
),
),
),
),
],
),
);
}
}
Take ListView or SingleChildScrollView under Body. And then use bottomNavigationBar in Scaffold.
Scaffold(
body: ListView(
children: [],
),
bottomNavigationBar: Container(
padding: MediaQuery.of(context).viewInsets,
color: Colors.grey[300],
child: Container(
padding: EdgeInsets.symmetric(vertical: 2),
margin: EdgeInsets.symmetric(horizontal: 5),
child: TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Type a message',
),
))
),
);
I think what you need is use a SingleChildScrollView, the content will scroll when the keyboard show up, will let you two examples:
// Flutter code sample for
// In this example, the children are spaced out equally, unless there's no more
// room, in which case they stack vertically and scroll.
//
// When using this technique, [Expanded] and [Flexible] are not useful, because
// in both cases the "available space" is infinite (since this is in a viewport).
// The next section describes a technique for providing a maximum height constraint.
import 'package:flutter/widgets.dart';
void main() => runApp(MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return WidgetsApp(
title: 'Flutter Code Sample',
builder: (BuildContext context, Widget navigator) {
return MyStatelessWidget();
},
color: const Color(0xffffffff),
);
}
}
/// This is the stateless widget that the main application instantiates.
class MyStatelessWidget extends StatelessWidget {
MyStatelessWidget({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return Scrollbar(
child: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Container(
// A fixed-height child.
color: const Color(0xff808000), // Yellow
height: 120.0,
),
Container(
// Another fixed-height child.
color: const Color(0xff008000), // Green
height: 120.0,
),
],
),
),
),
);
},
);
}
}
Another example for SingleChildScrollView
// Flutter code sample for
// In this example, the column becomes either as big as viewport, or as big as
// the contents, whichever is biggest.
import 'package:flutter/widgets.dart';
void main() => runApp(MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return WidgetsApp(
title: 'Flutter Code Sample',
builder: (BuildContext context, Widget navigator) {
return MyStatelessWidget();
},
color: const Color(0xffffffff),
);
}
}
/// This is the stateless widget that the main application instantiates.
class MyStatelessWidget extends StatelessWidget {
MyStatelessWidget({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: IntrinsicHeight(
child: Column(
children: <Widget>[
Container(
// A fixed-height child.
color: const Color(0xff808000), // Yellow
height: 120.0,
),
Expanded(
// A flexible child that will grow to fit the viewport but
// still be at least as big as necessary to fit its contents.
child: Container(
color: const Color(0xff800000), // Red
height: 120.0,
),
),
],
),
),
),
);
},
);
}
}
Try this: Go to your AndroidManifest.xml and remove:
android:windowSoftInputMode="adjustResize" under the application-activity tag.
Basically, just change this:
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="#style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
to this:
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="#style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
>
Worked for one of my apps.
This is the correct way to do this:
Widget _buildContent(BuildContext context) {
return Stack(
children: [
Column(
children: [
Expanded(
child: YOUR_SCROLLING_AREA_HERE,
),
YOUR_PINNED_WIDGET_HERE,
],
),
],
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Title'),
),
body: _buildContent(context),
);
}