flutter 2 SliverPersistentHeader - avoid overlap during scroll - flutter

Here is my code :
import 'package:all_in_one/cooking/pages/recipe/header/search_bar_header.dart';
import 'package:all_in_one/cooking/pages/recipe/header/welcome_header.dart';
import 'package:flutter/material.dart';
class MyRecipePage extends StatefulWidget {
final String title;
MyRecipePage({Key? key, required this.title}) : super(key: key);
#override
_MyRecipePageState createState() => _MyRecipePageState();
}
class _MyRecipePageState extends State<MyRecipePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
WelcomeHeader(),
SearchBarHeader(),
SliverGrid.count(),
],
) ,
);
}
}
class WelcomeHeader extends StatelessWidget {
#override
Widget build(BuildContext context) {
return
SliverPersistentHeader(
floating: true,
delegate: SliverAppBarDelegate(
minHeight: 0,
maxHeight: 100,
child: Container(
color: Colors.white,
child: _MyWelcomingHeader(),
),
),
);
}
}
class _MyWelcomingHeader extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: [
Flexible(
child: CircleAvatar(
radius: 57,
backgroundColor: Colors.grey.shade50,
child: Image.asset("assets/emoji-food.jpg"),
),
),
Flexible(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
'Enjoy the recipes',
style: TextStyle(
color: Colors.black,
fontSize: 26.0,
fontWeight: FontWeight.bold,
),
),
),
),
],
);
}
}
class SearchBarHeader extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SliverPersistentHeader(
pinned: true,
delegate: SliverAppBarDelegate(
minHeight: 50,
maxHeight: 50,
child: Container(
color: Colors.white,
child: _MySearchBar(),
),
),
);
}
}
class _MySearchBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(width: 10),
const Icon(Icons.search, color: Colors.grey, size: 30),
const SizedBox(width: 5),
Text("Search product",
style: TextStyle(
color: Colors.grey.shade500, fontSize: 12, fontWeight: FontWeight.w200))
],
);
}
}
The code of the silver bar Delegate is from this stackoverflow post
import 'package:flutter/material.dart';
import 'dart:math' as math;
class SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
final double minHeight;
final double maxHeight;
final Widget child;
SliverAppBarDelegate({
required this.minHeight,
required this.maxHeight,
required this.child,
});
#override
double get minExtent => minHeight;
#override
double get maxExtent => math.max(maxHeight, minHeight);
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return SizedBox.expand(child: child);
}
#override
bool shouldRebuild(SliverAppBarDelegate oldDelegate) {
return maxHeight != oldDelegate.maxHeight ||
minHeight != oldDelegate.minHeight ||
child != oldDelegate.child;
}
}
My goal is to have 2 SliverPersistentHeader, one pinned and one floating.
The one floating (the 1st one) should resize the text and the image while scrolling..
In the following screenshot, we can see that the 2nd SliverPersistentHeader is overlapping the 1st one.
How can I do to make the Text resize itself. I try to use Flexible like I did for the CircleAvatar but I can't succeed :/
Thanks

I found a solution using the opacity, so my WelcomeHeader becomes :
import 'package:flutter/material.dart';
import 'dart:math' as math;
class WelcomeHeader extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SliverAppBar(
backgroundColor: Colors.white,
pinned: false,
floating: false,
snap: false,
expandedHeight: 120,
flexibleSpace: _MyWelcomingHeader()
);
}
}
class _MyWelcomingHeader extends StatelessWidget {
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, c) {
final settings = context
.dependOnInheritedWidgetOfExactType<FlexibleSpaceBarSettings>();
final deltaExtent = settings!.maxExtent - settings.minExtent;
final t =
(1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent)
.clamp(0.0, 1.0);
final fadeStart = math.max(0.0, 1.0 - kToolbarHeight / deltaExtent);
const fadeEnd = 1.0;
final opacity = 1.0 - Interval(fadeStart, fadeEnd).transform(t);
return Padding(
padding: const EdgeInsets.all(8.0),
child: Opacity(
opacity: opacity,
child: Column(
children: [
Flexible(
child: CircleAvatar(
radius: 57,
backgroundColor: Colors.grey.shade50,
child: Image.asset("assets/emoji-food.jpg"),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Enjoy the recipes !',
style: TextStyle(
color: Colors.black,
fontSize: 26.0,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
);
});
}
}

Related

How to resize a widget on user action in flutter

I'm trying to make a hight picker and using a image along with the number of height.i want when user pick any height number the size of image increase or decrease according to the selected Height number.
Try as follows:
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var items = [
200,
300,
400,
];
var index=0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Test"),
),
body: Column(
children:[
ListView.builder(
shrinkWrap:true,
itemCount:items.length,
itemBuilder:(ctx,i){
return TextButton(onPressed:(){
setState((){
index=i;
});
},child:Text(items[i].toString()));
}
),
Image.network("https://images.unsplash.com/photo-1453728013993-6d66e9c9123a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8dmlld3xlbnwwfHwwfHw%3D&w=1000&q=80",
height:items[index].toDouble()
),
]
)
);
}
}
Please refer to below code example
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final TextEditingController heightControl = TextEditingController();
final ValueNotifier<double> height1 = ValueNotifier<double>(200);
final ValueNotifier<double> _height2 = ValueNotifier<double>(120);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: AnimatedBuilder(
animation: Listenable.merge([
height1,
_height2,
]),
child: null,
builder: (BuildContext context, _) {
return Stack(
children: <Widget>[
Positioned(
left: 30,
right: 30,
bottom: 10,
child: Column(
children: <Widget>[
InkWell(
onTap: () {
height1.value = 80.0;
},
child: Container(
color: Colors.transparent,
height: height1.value,
child: Stack(
clipBehavior: Clip.none,
children: [
Image.network(
'https://images.stockfreeimages.com/2911/sfi226w/29118870.jpg',
fit: BoxFit.fill,
height: height1.value,
),
Text(
"Current Height ${height1.value} \n Tap to reduce height",
style: TextStyle(
color: Colors.white,
),
),
],
),
),
),
InkWell(
onTap: () {
_height2.value = 290.0;
},
child: Container(
color: Colors.orange,
height: _height2.value,
child: Stack(
clipBehavior: Clip.none,
children: [
Image.network(
'https://images.stockfreeimages.com/5439/sfi226w/54392167.jpg',
fit: BoxFit.fill,
height: _height2.value,
),
Text(
'Current height : ${_height2.value} \n Tap to increase height'),
],
),
),
),
],
),
),
],
);
},
),
);
}
}

A RenderFlex overflowed by 141 pixels on the bottom

I added a bottom navigation bar on the bottom of the screen but it has interfered with the overflowed pixels. A part of my code which is the CartTotal() wasnt shown in the code. I tried using Listview but im not able to add both of the items in the column for both CartProducts() and CartTotal(). This was the suggestion:
Consider applying a flex factor (e.g. using an Expanded widget) to force the children of the RenderFlex to fit within the available space instead of being sized to their natural size.
This is considered an error condition because it indicates that there is content that cannot be seen. If the content is legitimately bigger than the available space, consider clipping it with a ClipRect widget before putting it in the flex, or using a scrollable container rather than a Flex, like a ListView.
import 'package:flutter/material.dart';
import '../../widgets/cart_products.dart';
import '../../widgets/cart_total.dart';
import 'package:grocery_shopping/widgets/widgets.dart';
import '../Home/components/bottom_bar.dart';
import '../Home/components/enum.dart';
class CartScreen extends StatelessWidget {
const CartScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Your Cart")),
bottomNavigationBar: const CustomBottomBar(selectMenu: MenuState.cart),
body: Column(
children: [
CartProducts(),
CartTotal(),
],
),
);
}
}
this is my CartProducts()
import 'package:flutter/material.dart';
import 'package:grocery_shopping/controllers/cart_controller.dart';
import 'package:grocery_shopping/models/product_model.dart';
import 'package:get/get.dart';
class CartProducts extends StatelessWidget {
final CartController controller = Get.find();
CartProducts({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Obx(
() => SizedBox(
height: 600,
child: ListView.builder(
itemCount: controller.products.length,
itemBuilder: (BuildContext context, int index) {
return CartProductCard(
controller: controller,
product: controller.products.keys.toList()[index],
quantity: controller.products.values.toList()[index],
index: index,
);
}),
),
);
}
}
class CartProductCard extends StatelessWidget {
final CartController controller;
final Product product;
final int quantity;
final int index;
const CartProductCard({
Key? key,
required this.controller,
required this.product,
required this.quantity,
required this.index,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20.0,
vertical: 10,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CircleAvatar(
radius: 40,
backgroundImage: NetworkImage(
product.imageUrl,
),
),
SizedBox(
width: 20,
),
Expanded(
child: Text(product.name),
),
IconButton(
onPressed: () {
controller.removeProduct(product);
},
icon: Icon(Icons.remove_circle),
),
Text('$quantity'),
IconButton(
onPressed: () {
controller.addProduct(product);
},
icon: Icon(Icons.add_circle),
),
],
),
);
}
}
This is my CartTotal()
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:grocery_shopping/controllers/cart_controller.dart';
import 'package:get/get.dart';
class CartTotal extends StatelessWidget {
final CartController controller = Get.find();
CartTotal({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Obx(
() => Container(
padding: const EdgeInsets.symmetric(horizontal: 75),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Total',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
Text(
'\RM${controller.total}',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
],
)),
);
}
}

Stateful widget doesn't change state

In this code, when I change page (I'm using PageView as is it in code below) flutter doesn't trigger rebuild, so condition if(_page == 1) will take effect after I press "hot reload". Any tips for solution? I calling this class in main.dart (HomePage) which is Stateless widget. Could it be the problem?
Thanks for any help!
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
int _page = 0;
class Guide extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new GuideState();
}
}
class GuideState extends State<Guide> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(children: [
if (_page == 1)
Padding(
padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
child: Image(
image: AssetImage('graphics/Logo.png'),
height: MediaQuery.of(context).size.height * 0.1)),
SizedBox(height: 500, child: Page()),
]))));
}
}
class Page extends StatefulWidget {
PageState createState() => PageState();
}
class PageState extends State<Page> {
final controller = PageController(
initialPage: 0,
);
#override
Widget build(BuildContext context) {
return Scaffold(
//appBar: AppBar(title: Text('PageView Widget in Flutter')),
body: Center(
child: Container(
width: MediaQuery.of(context).size.width * 0.95,
height: MediaQuery.of(context).size.height * 0.6,
child: PageView(
controller: controller,
onPageChanged: (page) {
setState(() {
if (page == 1) {
_page = 1;
}
});
},
pageSnapping: true,
scrollDirection: Axis.horizontal,
children: <Widget>[
Container(
//color: Colors.pink,
//child: Center(
child: Text(
'1. Tento text bude nahrán z databáze.',
style: TextStyle(fontSize: 25, color: Colors.black),
)),
Container(
//color: Colors.green,
child: Text(
'2. Tento text bude nahrán z databáze',
style: TextStyle(fontSize: 25, color: Colors.black),
)),
Container(
// color: Colors.lightBlue,
child: Text(
'3. Tento text bude nahrán z databáze',
style: TextStyle(fontSize: 25, color: Colors.black),
)),
],
),
)));
}
}
The variable _page is set as global, it has to be part of as state inorder to trigger changes, but in your case you want to change a widget base on action in another child widget, this can be done in several ways depending on your choice the easies in you case is to have a function as a parameter for your child widget Page :
class Page extends StatefulWidget {
final Function(int) onChange;
const Page({Key key, this.onChange}) : super(key: key);
PageState createState() => PageState();
}
and then call it when the page change
onPageChanged: (page) {
widget.onChange(page);
},
so with this you can handle the change in you parent widget and trigger state change
class GuideState extends State<Guide> {
int _page = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
children:[
if (_page == 1)
Padding(
padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
child: Image(
image: AssetImage('graphics/Logo.png'),
height: MediaQuery.of(context).size.height * 0.1,
),
),
SizedBox(
height: 500,
child: Page(
onChange: (page) {
setState(() => _page = page);
},
),
),
],
),
),
),
);
}
}
int _page = 0; is not part of the state of your Guide widget. Place it here:
class GuideState extends State<Guide> {
int _page = 0;
...

Keyboard automatically disappears from TextField in ListView.Builder

I'm trying to implement a solution where a row (containing both a TextField and a Text) in ListView.Builder is automatically for every record retrieved from a webserver.
However when I want to start typing in such a TextField the keyboard appears and immediatly disappears again.
This is the code of my screen.
class GameScreen extends StatelessWidget {
static const RouteName = "/GameScreen";
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
const horizontalMargin = 20.0;
return Scaffold(
appBar: getAppBar(),
backgroundColor: Colors.transparent,
body: Stack(
children: <Widget>[
Background(),
Column(
children: <Widget>[
Header("Starting letter: B"),
Expanded(
child: ListBlocProvider(
listWidget: GameCategoriesList(),
itemsService: CategoriesService(),
margin: EdgeInsets.only(
left: horizontalMargin,
bottom: 10,
right: horizontalMargin,
),
),
),
SizedBox(
height: 20,
),
SizedBox(
width: size.width - 40,
height: 60,
child: Container(
height: 60,
child: TextButtonWidget(
() {
// Navigator.of(context).pushNamed(GameScreen.RouteName);
},
"Stop game",
),
),
),
SizedBox(
height: 20,
)
],
),
],
),
);
}
}
This is the code of my ListBlocProvider:
class ListBlocProvider extends StatelessWidget {
final ListWidget listWidget;
final ItemsService itemsService;
final bool useColor;
final bool usePaddingTop;
final double height;
final EdgeInsets margin;
const ListBlocProvider({
#required this.listWidget,
#required this.itemsService,
this.useColor = true,
this.usePaddingTop = true,
this.height = 200,
this.margin,
});
#override
Widget build(BuildContext context) {
const horizontalMargin = 20.0;
return BlocProvider(
create: (context) => ItemsBloc(itemsService: itemsService)..add(ItemsFetched()),
child: Container(
padding: usePaddingTop ? EdgeInsets.only(top: 10) : null,
decoration: BoxDecoration(
color: this.useColor ? Color.fromRGBO(10, 50, 75, 0.9) : null,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10),
),
),
margin: this.margin,
height: this.height,
child: this.listWidget,
),
);
}
}
This is the code of my List:
class GameCategoriesList extends ListWidget {
#override
_GameCategoriesListState createState() => _GameCategoriesListState();
}
class _GameCategoriesListState extends State<GameCategoriesList> {
#override
Widget build(BuildContext context) {
return BlocBuilder<ItemsBloc, ItemsState>(
builder: (context, state) {
if (state is ItemsFailure) {
return Center(
child: Text('failed to fetch categories'),
);
}
if (state is ItemsSuccess) {
if (state.items.isEmpty) {
return Center(
child: Text('no categories found.'),
);
}
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
var textEditingController = TextEditingController();
return GameCategoryItemWidget(
key: UniqueKey(),
categoryModel: state.items[index],
textEditingController: textEditingController,
);
},
itemCount: state.items.length,
);
}
return Center(
child: LoadingIndicator(),
);
},
);
}
}
And this is the code where the both the TextField and the Text are build:
class GameCategoryItemWidget extends StatefulWidget {
final CategoryModel categoryModel;
final TextEditingController textEditingController;
const GameCategoryItemWidget({Key key, this.categoryModel, this.textEditingController}) :
super(key: key);
#override
_GameCategoryItemWidgetState createState() => _GameCategoryItemWidgetState();
}
class _GameCategoryItemWidgetState extends State<GameCategoryItemWidget> {
var formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Container(
child: Form(
key: this.formKey,
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 10, top: 20, bottom: 10),
child: Text(
this.widget.categoryModel.name,
style: TextStyle(
color: Colors.white,
fontSize: 18,
),
),
),
Container(
color: Colors.white,
child: InputField(
InputDecoration(labelText: this.widget.categoryModel.name),
this.widget.textEditingController,
false,
),
),
],
),
),
);
}
#override
void dispose() {
this.widget.textEditingController.dispose();
super.dispose();
}
}
The InputField is a custom widget to hide the switch between a Material and a Cupertino version of the TextField.
I've already tried to remove the Key from the custom TextField widget. The funny part is that the input is actually working, however it can't determine for which of the TextFields in the ListView the input is determined so it adds the input to all of them. I've also tried to swap things around with making Stateless widgets Statefull, but that didn't help either.
The entire build is based upon: https://bloclibrary.dev/#/flutterinfinitelisttutorial.
Hoping you guys can help me.

How to make scrollable Drag and Drop in Flutter?

I am trying to make a scrollable and zoomable Stack using SingleChildScrollView in order to implement a canvas editor.
The Drag and Drop works perfectly when I put my dragabble item in the initial view but when I scroll down the view and I tried to drop my container is coming back in the initial view.
I'm new to the Flutter development so maybe I missunderstood in the implementation of a such thing.
Here's the code I currently have.
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: App(),
),
);
}
}
class App extends StatefulWidget {
#override
AppState createState() => AppState();
}
class AppState extends State<App> {
Color caughtColor = Colors.grey;
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Stack(
children: <Widget>[
Container(
height: 2000,
),
DragBox(Offset(0.0, 0.0), 'Box One', Colors.blueAccent),
],
),
);
}
}
class DragBox extends StatefulWidget {
final Offset initPos;
final String label;
final Color itemColor;
DragBox(this.initPos, this.label, this.itemColor);
#override
DragBoxState createState() => DragBoxState();
}
class DragBoxState extends State<DragBox> {
Offset position = Offset(0.0, 0.0);
#override
void initState() {
super.initState();
position = widget.initPos;
}
#override
Widget build(BuildContext context) {
return Positioned(
left: position.dx,
top: position.dy,
child: Draggable(
data: widget.itemColor,
child: Container(
width: 100.0,
height: 100.0,
color: widget.itemColor,
child: Center(
child: Text(
widget.label,
style: TextStyle(
color: Colors.white,
decoration: TextDecoration.none,
fontSize: 20.0,
),
),
),
),
onDraggableCanceled: (velocity, offset) {
setState(() {
position = offset;
});
},
feedback: Container(
width: 120.0,
height: 120.0,
color: widget.itemColor.withOpacity(0.5),
child: Center(
child: Text(
widget.label,
style: TextStyle(
color: Colors.white,
decoration: TextDecoration.none,
fontSize: 18.0,
),
),
),
),
));
}
}
Any suggestions or code samples would be really helpful to me.
I solve the problem like this, wrap your draggable with a listener.
Listener listenableDraggable = Listener(
child: draggable,
onPointerMove: (PointerMoveEvent event) {
if (event.position.dy > MediaQuery.of(context).size.height) {
// 120 is height of your draggable.
scrollController.scrollTo(scrollcontroller.offset + 120);
}
},
);