How to implement Popup with Flutter? - flutter

I have a Flutter app with screens rendered conditionally with an array. Anyway, I need to have a popup screen like this :
If have stored all my "popup screens" in an array and rendered the main screen and the popup screen in a stack. I don't know if this is the best solution and I guess I will have performance issues.
Here is the PopupContainerclass, this Widget is rendered on every Popup Screen with the child passed as content :
class PopupContainer extends StatefulWidget {
final Widget? child;
const PopupContainer({
Key? key,
this.child,
}) : super(key: key);
#override
State<PopupContainer> createState() => _PopupContainerState();
}
class _PopupContainerState extends State<PopupContainer> {
#override
Widget build(BuildContext context) {
final height = MediaQuery.of(context).size.height;
return Consumer<ScreenManager>(
builder: (context, manager, child) => Stack(
alignment: Alignment.bottomCenter,
children: [
BackdropFilter(
filter: ImageFilter.blur(sigmaX: 6, sigmaY: 6),
child: Container(
decoration: BoxDecoration(color: Colors.white.withOpacity(0.0)),
),
),
Container(
height: height * 0.8,
width: double.infinity,
padding: const EdgeInsets.all(32),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
boxShadow: [
BoxShadow(
blurRadius: 37,
spreadRadius: 0,
color: Color.fromRGBO(28, 48, 72, 0.24),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
alignment: Alignment.topRight,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.zero,
primary: Colors.transparent,
shadowColor: Colors.transparent,
),
onPressed: () => manager.closePopup(),
child: SvgPicture.asset('assets/close.svg'),
),
),
widget.child ?? const SizedBox.shrink(),
],
),
),
],
),
);
}
}
The consumer is used for handling the screens states :
enum ScreensName {
homeScreen,
favoriteProductsScreen,
archivedListsScreen,
recipesScreen,
}
enum PopupsName {
newProductPopup,
archivedListPopup,
editProductPopup,
newRecipePopup,
}
const screens = <ScreensName, Widget>{
ScreensName.homeScreen: HomeScreen(),
ScreensName.favoriteProductsScreen: FavoriteProductsScreen(),
ScreensName.archivedListsScreen: ArchivedListsScreen(),
ScreensName.recipesScreen: RecipesScreen(),
};
const popups = <PopupsName, Widget>{
PopupsName.newProductPopup: NewProductPopup(),
};
class ScreenManager extends ChangeNotifier {
static ScreensName screenName = ScreensName.homeScreen;
static PopupsName? popupName = PopupsName.newProductPopup;
get currentScreen => screens[screenName];
get currentPopup => (popups[popupName] ?? Container());
/// Open the given popup.
void openPopup(PopupsName newPopupName) {
popupName = newPopupName;
notifyListeners();
}
/// Closes the current popup.
void closePopup() {
popupName = null;
notifyListeners();
}
/// Change the screen.
void setScreen(ScreensName newScreenName) {
screenName = newScreenName;
notifyListeners();
}
}
And finally, the main component build method (I also have some theme styling but useless here) :
Widget build(BuildContext context) {
DatabaseHelper.initDb();
return Consumer<ScreenManager>(
builder: (context, screenManager, child) => Material(
child: MaterialApp(
title: _title,
theme: _customTheme(),
home: Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
screenManager.currentScreen,
screenManager.currentPopup,
],
),
),
),
);
}
PS : I am a web developer so I know the main programming principles but Dart and mobile dev is brand new for me. Also, I could share my code with you, however, this project is splitted into files and it would take too much space in the post. Ask if you need it !

Maybe an easier solution would be to use the showDialog function where you need to trigger the popup. Check out the docs https://api.flutter.dev/flutter/material/showDialog.html
showDialog(context: context, builder: (context) => AlertDialog(title: Text('Title'), content: Text('Here content'),));

Related

Open Drawer of current (topmost) route's Scaffold

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();
}
}

Do not want rounded corners in the AppBar when the Sliver App Bar is collapsed

I'm trying to implement a layout, where the Sliver App Bar has rounded bottom corners when expanded, but when it is collapsed I do not want those rounded corners.
Actual Behaviour:
enter image description here
Expected Behaviour:
Here's my SliverAppBar code:
`SliverAppBar(
systemOverlayStyle: const SystemUiOverlayStyle(
statusBarColor: Color(0xFFE0E64B),
),
backgroundColor: Color(0xFFE0E64B),
expandedHeight: 300.0,
floating: false,
pinned: true,
collapsedHeight: 60.0,
onStretchTrigger: () async {
setState(() {});
},
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Text(
'Pokedex',
style: TextStyle(
color: Colors.white,
),
),
Text(
'#025',
style: TextStyle(
color: Colors.white,
),
),
],
),
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
background: Container(
decoration: const BoxDecoration(
color: Color(0xFFE0E64B),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(50.0),
bottomRight: Radius.circular(50.0),
),
),
child: Hero(
tag: 'pokemon_container$index',
child: Column(
children: [
const SizedBox(
height: 120.0,
),
Expanded(
child: ClipRRect(
child: Image.network(
imageUrl,
fit: BoxFit.scaleDown,
),
),
),
const SizedBox(
height: 30.0,
),
],
),
),
),
),
),`
shape: ContinuousRectangleBorder(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30))),
Here is your code. Put it inside sliverAppBar
NestedScrollView / SliverAppBar solution
This is definitely achievable. SliverAppBar does support what we need, it has support for rounded borders, the shadow effect and changing sizes. For handling the border requirement we can use a RoundedRectangleBorder.
Although for getting a smooth transition for the border change, we need to update the values frequently, when changing the size of the SliverAppBar.
Example code
Do note that the package flutter_riverpod (version 1.0.3) is used for state management in this example.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class RoundedSliverExampleScreen extends StatelessWidget {
const RoundedSliverExampleScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
floatHeaderSlivers: true,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
ExpandingAppBar(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Flexible is important for the children widgets added here.
Flexible(child: Container(color: Colors.yellow, width: 50, height: 50,))
],
)
];
},
body: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text("Hello!")
],
),
)
);
}
}
/// An SliverAppBar widget with alternating rounded border depending on the
/// expandedHeight.
///
/// Provides easy support for adding children widgets in the
/// expanded area as if it was a Column, although children widgets should be
/// wrapped in a Flexible widget.
class ExpandingAppBar extends ConsumerWidget {
const ExpandingAppBar({
Key? key,
this.children = const <Widget>[],
this.mainAxisAlignment = MainAxisAlignment.start
}) : super(key: key);
final List<Widget> children;
final MainAxisAlignment mainAxisAlignment;
#override
Widget build(BuildContext context, WidgetRef ref) {
RoundedHeaderState state = ref.watch(roundedHeaderProvider);
return SliverAppBar(
expandedHeight: state.highestHeight,
pinned: true,
primary: true,
forceElevated: true,
title: const Text('Pokèdex'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(bottom: Radius.circular(state.radius)),
),
flexibleSpace: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
// We update the state here.
ref.read(roundedHeaderProvider.notifier).updateHeight(constraints.maxHeight);
return Opacity(
opacity: state.scrollFraction,
child: Padding(
padding: EdgeInsets.only(top: state.smallestHeight),
child: Column(mainAxisAlignment: mainAxisAlignment, children: children),
),
);
},
),
);
}
}
#immutable
class RoundedHeaderState {
final double highestHeight = 256;
final double smallestHeight = kToolbarHeight + 24;
final double currentHeight;
final double contentOpacity = 1;
const RoundedHeaderState({this.currentHeight = 256});
double get scrollFraction => min(max((currentHeight - smallestHeight) / (highestHeight - smallestHeight), 0), 1);
double get radius => 64 * scrollFraction;
}
class RoundedHeaderNotifier extends StateNotifier<RoundedHeaderState> {
RoundedHeaderNotifier(): super(const RoundedHeaderState());
updateHeight(double currentHeight) {
final newState = RoundedHeaderState(currentHeight: currentHeight);
// Check that the new state is not equal to the next (prevents rebuild loop)
if(state.currentHeight != newState.currentHeight) {
// Setting state triggers an rebuild, the PostFrameCallback let Flutter
// postpone the upcoming rebuild at a later time.
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
state = newState;
});
}
}
}
final roundedHeaderProvider = StateNotifierProvider<RoundedHeaderNotifier, RoundedHeaderState>((ref) {
return RoundedHeaderNotifier();
});
// Pay attention to the ProviderScope wrapping the MaterialApp. Riverpod requires this.
void main() => runApp(
const ProviderScope(
child: MaterialApp(home: RoundedSliverExampleScreen())
)
);
Result - Gif of the SliverAppBar's transition.

How can I send data from bottom sheet to parent widget?

I have list of products in bottom sheet, when I choose any product I want to parent's widget to add it, unfortunately my product adds only after hot reload, or when I create a new route from bottom sheet to parent's widget, how can I solve this problem, any ideas? Here is the part of the bottom sheet code
class IceBottomSheet extends StatefulWidget {
const IceBottomSheet({Key? key}) : super(key: key);
#override
_IceBottomSheetState createState() => _IceBottomSheetState();
}
class _IceBottomSheetState extends State<IceBottomSheet> {
final _model = ProductWidgetsModel();
#override
Widget build(BuildContext context) {
List<Widget> productWidgetList = [];
products.forEach((product) =>
productWidgetList.add(SingleProductWidget(product: product)));
return Provider(
model: _model,
child: Expanded(
child: GridView.count(
crossAxisSpacing: 10,
mainAxisSpacing: 16,
shrinkWrap: true,
crossAxisCount: 2, children: productWidgetList),
),
);
}
}
class SingleProductWidget extends StatefulWidget {
final Product product;
const SingleProductWidget({Key? key, required this.product})
: super(key: key);
#override
State<SingleProductWidget> createState() => _SingleProductWidgetState();
}
class _SingleProductWidgetState extends State<SingleProductWidget> {
#override
Widget build(BuildContext context) {
final model = Provider.of(context)?.model;
return Padding(
padding: const EdgeInsets.all(5.0),
child:
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(
width: double.infinity,
height: 100,
child: DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(
image: widget.product.image,
),
shape: BoxShape.circle,
border: Border.all(
color: model?.idSelected == widget.product.id
? Colors.yellow
: Colors.grey,
width: 5.0,
style: BorderStyle.solid,
),
),
child: GestureDetector(
onTap: () {
model?.idSelected = widget.product.id;
// Route route =
// MaterialPageRoute(builder: (context) => BerryPage(context,));
// Navigator.push(context, route);
if(model?.idSelected == 1){
menuRow.removeAt(2);
Navigator.pop(context);
choice.insert(2, Adds(id: 102, name: 'Холодок', img: 'https://autogear.ru/misc/i/gallery/73434/2759438.jpg'));
}
}),
),
),
And here is the part of parent's widget code, it is inside GestureDetector
else if (index == 2){
setState(() {
});
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (BuildContext builder) {
return Container(
height: 250,
child: Column(
children: [
SizedBox(
height: 10,
),Row(
children: [
Padding(
padding: EdgeInsets.only(left: MediaQuery.of(context).size.width*0.25 + MediaQuery.of(context).size.width *0.12),
child: Text(
'Холодок',
style: TextStyle(
fontFamily: 'Newfont',
fontSize: 22,
),
),
),
SizedBox(width: MediaQuery.of(context).size.width*0.25,),
IconButton(icon: Icon(Icons.close),onPressed: (){Navigator.pop(context);},)
],
),
Divider(),
IceBottomSheet(),
],
));
},
);
So when you open the BottomSheet you have to add await before it, so when you call Navigator.pop(context, data_you_want_to_pass_to_parent) it will wait for some data to be returned.
final data = await openBottomSheet();
inside the bottomSheet when you want to close, just pass the the desired data as so
Navigator.pop(context, data_you_want_to_pass_to_parent);

Is it possible to resume the state of a certain widget page in flutter after popping the page out?

Let's say for example when I press on the Like button (from the Like_button package) it will set its boolean to true, meaning that it will turn red. But when I pop this page out of the navigation and go back to it, how do I get it to show that I have previously pressed the button already? (right now what it does is it shows the same state as if i didn't press on it before) I want to make it into like 'favourite article' system.
import 'package:flutter/material.dart';
import 'package:like_button/like_button.dart';
class Article extends StatefulWidget {
#override
_ArticleState createState() => _ArticleState();
}
class _ArticleState extends State<Article> {
bool isLiked = false;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.green[50],
body: SingleChildScrollView(
GestureDetector(
onTap: () {
Navigator.pushNamed(context, '/N1');
},
child: Container(
margin: const EdgeInsets.symmetric(vertical:10),
child: Padding(
padding: EdgeInsets.fromLTRB(30, 0, 30, 0),
child: Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25.0)),
child: Column(
children: [
Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(25.0),
child: Image.asset('assets/CoverImage1.jpeg')),
Positioned(
top: 15,
right: 15,
child: Container(
width:50,
height:50,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(15.0)),
border: Border.all(color: Color.fromRGBO(141, 141, 141, 1.0).withAlpha(40)),
),
child: Center(
child: LikeButton(
size: 25,
isLiked: isLiked,
likeBuilder: (isLiked) {
final color = isLiked ? Colors.red : Colors.grey;
return Icon(Icons.favorite, color:color, size:25);
},
onTap: (isLiked) async {
this.isLiked = !isLiked;
return !isLiked;
}
),
),
),
),
],
),
you can store the value of variable with shared preferences.
you can check the documentation here: https://pub.dev/packages/shared_preferences/example
try these steps:
store value with something like prefs.setBool()
get the value with getBool in initState(). initState() always perform before UI widget is built.
with these steps enables the app to get the value even when apps is closed

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),
);
}