Allow widget to overflow - flutter

I would like to create my own safe area view but instead of just cutting off the overflown area, I would like to have the overflowing area blocked by a semi transparent background.
I realise this question is somewhat similar to Allowing a widget to overflow to another widget but the answer there can solve that question in particular but not mine.
So how do I make the flutter widget overflow?
I used stack to place a container above the view when there's a top overflow and below if there's a bottom overflow. The result works good except that its overflowing to the semi transparent background.
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Didian',
home: Scaffold(
body: OpacitySafeArea(
child: RandomWords()
),
),
);
}
}
class OpacitySafeArea extends StatelessWidget {
final Widget child;
OpacitySafeArea({
#required this.child
});
#override
Widget build(BuildContext context) {
var deviceData = MediaQuery.of(context);
var height = deviceData.size.height;
var width = deviceData.size.width;
var top = deviceData.padding.top;
var bottom = deviceData.padding.bottom;
print(deviceData);
return Stack(
overflow: Overflow.visible,
children: <Widget>[
this.child,
top > 0? Container(
color: Color.fromRGBO(255, 255, 255, 0.9),
height: top,
width: width
):null,
bottom > 0? Positioned(
bottom: 0,
child: Container(
color: Color.fromRGBO(255, 255, 255, 0.9),
height: bottom,
width: width
)
):null
].where((child) => child != null).toList()
);
}
}
class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
Widget buildSuggestions() {
return ListView.separated(
padding: EdgeInsets.all(16),
itemCount: 100,
itemBuilder: (context, index) {
if(index >= _suggestions.length) {
_suggestions.add(WordPair.random());
}
return ListTile(
title: Text(
_suggestions[index].asPascalCase,
style: TextStyle(fontSize: 18)
)
);
},
separatorBuilder: (context, index) => Divider(),
);
}
#override
Widget build(BuildContext context) {
return buildSuggestions();
}
}
class RandomWords extends StatefulWidget {
#override
RandomWordsState createState() => RandomWordsState();
}

I was looking for same, and finally used a simple solution without using SafeArea():
Just apply a padding to ListView itself, which you can get using MediaQuery.of(context).viewPadding.bottom
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
padding: EdgeInsets.all(0).copyWith(bottom: MediaQuery.of(context).viewPadding.bottom),
itemCount: 2000,
itemBuilder: (BuildContext context, int index) {
return Text("Item: $index");
},
),
);
}

Related

Animation when changing 'home:' content

class Segunda extends StatefulWidget {
Tercera createState() => Tercera();
}
class Tercera extends State<Segunda> {
var size, heightA, widthA;
List<StatefulWidget> bodys = [Segunda2(), Segunda3()];
int n = 0;
#override
Widget build(BuildContext context) {
setState(() {
size = MediaQuery.of(context).size;
heightA = size.height;
widthA = size.width;
});
return Scaffold(
body: Container(
width: widthA,
height: heightA,
child: Column(children: [
Container(
width: widthA,
height: heightA * 0.1,
color: Colors.blue,
child: ElevatedButton(
onPressed: () {
setState(() {
n++;
});
},
child: Text("Change")),
),
Container(
width: widthA,
height: heightA * 0.9,
child: MaterialApp(
home: bodys[n],
),
)
]),
));
}
}
I want to always have a little bar at the beginning of my app, but changing the content (im learning flutter and i want to make a little game).
So, I made a test app like this
that changes the content when you press a button, I have a list with different StatefulWidgets (Segunda2 and Segunda3 returns just a solid background color).
Is there anyway I can add a animation when changing the content of the 'home', like the ones you can do with Navigator (the new content sliding from the left, for example)
Im using this way because when I try to use navigator to change between classes while trying to have a permanent widget (like the blue bar in this case) it just ignores it and changes the whole thing, I want to press a button and see the new content coming from a side.
i tried using navigator to change content with an animation, but the persistent widget that i want to have just changes as well.
I tried using persistent widget perse, and it didnt work for me
Dont use Multiple MaterialApp and you can use PageView widget.
Try to follow this widget structure.
void main() => runApp(ProviderScope(child: MyApp()));
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Segunda(),
);
}
}
class Segunda extends StatefulWidget {
Tercera createState() => Tercera();
}
class Tercera extends State<Segunda> {
var size, heightA, widthA;
// List<StatefulWidget> bodys = [Segunda2(), Segunda3()];
List<Widget> bodys = [Text("Segunda2"), Text("Segunda3")];
final PageController controller = PageController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: LayoutBuilder(builder: (_, constraints) {
heightA = constraints.maxHeight;
widthA = constraints.maxWidth;
return Container(
width: widthA,
height: heightA,
child: Column(children: [
Container(
width: widthA,
height: heightA * 0.1,
color: Colors.blue,
child: ElevatedButton(
onPressed: () {
// setState(() {});
// controller.animateToPage(page,
// duration: duration, curve: curve);
controller.nextPage(
duration: Duration(milliseconds: 300),
curve: Curves.ease);
},
child: Text("Change")),
),
Expanded(
child: PageView.builder(
controller: controller,
itemBuilder: (context, index) {
return bodys[index];
},
),
)
]),
);
}),
);
}
}

Flutter Nested list scroll parent when reach to end/start of inner list

I am implementing a nested list in Flutter where need to start scrolling the parent list when it reach to the end/start the inner list. I tried with several ways, no one lucks. This one is the last approach i tried with.
This works for top to bottom , but when i scroll bottom to top, it does not(parent-child scroll is not smooth). May be some logical improvement require when scrolling bottom to top.
As i am new to Dart, could anyone help me to get this done ?
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(title: 'Flutter Demo', home: MyListView());
}
}
class MyListView extends StatelessWidget {
ScrollController _mainScrollController = ScrollController();
double listHeight = 370;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AppBar'),
),
body: Container(
height: MediaQuery.of(context).size.height,
child: ListView(
controller: _mainScrollController,
children: <Widget>[
Container(height: listHeight,child: RapportList(parentScrollController: _mainScrollController)),
OtherElement(text: "Other element 1 which will be scrolled",),
OtherElement(text: "Other element 2 which will be scrolled",),
OtherElement(text: "Other element 3 which will be scrolled",),
OtherElement(text: "Other element 4 which will be scrolled",),
OtherElement(text: "Other element 5 which will be scrolled",),
Container(height: listHeight,child: RapportList(parentScrollController: _mainScrollController)),
OtherElement(text: "Other element 4 which will be scrolled",),
OtherElement(text: "Other element 5 which will be scrolled",),
Container(height: listHeight,child: RapportList(parentScrollController: _mainScrollController)),
],
),
),
);
}
}
class RapportList extends StatefulWidget {
final ScrollController parentScrollController;
RapportList({#required this.parentScrollController});
#override
_RapportListState createState() => _RapportListState();
}
class _RapportListState extends State<RapportList> {
ScrollPhysics physics = ScrollPhysics();
// NeverScrollableScrollPhysics()
ScrollController _listViewScrollController;
void listViewScrollListener(){
print("smth");
if(_listViewScrollController.offset >= _listViewScrollController.position.maxScrollExtent &&
!_listViewScrollController.position.outOfRange){
if(widget.parentScrollController.offset==0){
widget.parentScrollController.animateTo(50,duration: Duration(milliseconds: 200),curve: Curves.linear);
}
setState((){
physics = NeverScrollableScrollPhysics();
});
print("bottom");
}
}
void mainScrollListener(){
if(widget.parentScrollController.offset <= widget.parentScrollController.position.minScrollExtent &&
!widget.parentScrollController.position.outOfRange){
setState((){
if(physics is NeverScrollableScrollPhysics){
physics = ScrollPhysics();
_listViewScrollController.animateTo(_listViewScrollController.position.maxScrollExtent-50,duration: Duration(milliseconds: 200),curve: Curves.linear);
}
});
print("top");
}
}
#override
void setState(fn) {
super.setState(fn);
}
#override
void initState() {
_listViewScrollController = ScrollController();
_listViewScrollController.addListener(listViewScrollListener);
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
widget.parentScrollController.addListener(mainScrollListener);
return ListView.builder(
controller: _listViewScrollController,
physics: physics,
shrinkWrap: true,
itemCount: 50,
itemBuilder: (context, index) {
return ListTile(
title: GestureDetector(
child: Row(
children: <Widget>[
Container(child: Text("text $index")),
],
),
),
);
},
);
}
}
class OtherElement extends StatelessWidget {
final String text;
OtherElement({this.text});
#override
Widget build(BuildContext context) {
return Container(
height: 100,
child: Center(child: Padding(
padding: const EdgeInsets.symmetric(horizontal:40.0),
child: Text(this.text,style:TextStyle(fontSize: 30)),
)),
);
}
}
The idea is taken completely from here : Flutter listview at the end of its content scrolls screen
you can try the following code,
1, add child ListView 's scroll listening condition
if ((currentOffset >= maxBound || currentOffset <= minBound) && !_listViewScrollController.position.outOfRange)
2 remove the unnecessary parent ListView auto scrolling code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(title: 'Flutter Demo', home: MyListView());
}
}
class MyListView extends StatelessWidget {
ScrollController _mainScrollController = ScrollController();
double listHeight = 370;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AppBar'),
),
body: Container(
height: MediaQuery.of(context).size.height,
child: ListView(
controller: _mainScrollController,
children: <Widget>[
Container(
height: listHeight, child: RapportList(_mainScrollController)),
OtherElement(
"Other element 1 which will be scrolled",
),
OtherElement(
"Other element 2 which will be scrolled",
),
OtherElement(
"Other element 3 which will be scrolled",
),
OtherElement(
"Other element 4 which will be scrolled",
),
OtherElement(
"Other element 5 which will be scrolled",
),
],
),
),
);
}
}
class RapportList extends StatefulWidget {
final ScrollController parentScrollController;
RapportList(this.parentScrollController);
#override
_RapportListState createState() => _RapportListState();
}
class _RapportListState extends State<RapportList> {
late ScrollController _listViewScrollController = ScrollController()
..addListener(listViewScrollListener);
ScrollPhysics _physics = ScrollPhysics();
// NeverScrollableScrollPhysics()
void listViewScrollListener() {
double currentOffset = _listViewScrollController.offset;
double maxBound = _listViewScrollController.position.maxScrollExtent;
double minBound = _listViewScrollController.position.minScrollExtent;
print("_listViewScrollController.offset : ${currentOffset} ___ ");
print(" _listViewScrollController.position.maxScrollExtent : ${maxBound}");
print(" _listViewScrollController.position.minScrollExtent : ${minBound}");
print("/n/n/n");
if ((currentOffset >= maxBound || currentOffset <= minBound) &&
!_listViewScrollController.position.outOfRange) {
setState(() {
_physics = NeverScrollableScrollPhysics();
});
print("bottom");
}
}
void mainScrollListener() {
if (widget.parentScrollController.offset <=
widget.parentScrollController.position.minScrollExtent &&
!widget.parentScrollController.position.outOfRange) {
setState(() {
if (_physics is NeverScrollableScrollPhysics) {
_physics = ScrollPhysics();
}
});
print("top");
}
}
#override
Widget build(BuildContext context) {
widget.parentScrollController.addListener(mainScrollListener);
return ListView.builder(
controller: _listViewScrollController,
physics: _physics,
shrinkWrap: true,
itemCount: 50,
itemBuilder: (context, index) {
return ListTile(
title: GestureDetector(
child: Row(
children: <Widget>[
Container(child: Text("text $index")),
],
),
),
);
},
);
}
}
class OtherElement extends StatelessWidget {
final String text;
OtherElement(this.text);
#override
Widget build(BuildContext context) {
return Container(
height: 100,
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 40.0),
child: Text(this.text, style: TextStyle(fontSize: 30)),
)),
);
}
}

How to mask-out the overlaped section, visible through the "translucent header sliver" in the NestedScrollView?

The following code yields a scrollable list together with a "translucent pinned sliver header".
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return [
SliverPersistentHeader(
delegate: _SliverPersistentHeaderDelegate(),
pinned: true,
),
];
},
body: ListView.builder(
itemBuilder: (context, index) {
return ListTile(
title: Container(
color: Colors.amber.withOpacity(0.3),
child: Text('Item $index'),
),
);
},
),
),
),
);
}
}
class _SliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
#override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
color: Colors.blue.withOpacity(0.75),
child: Placeholder(),
);
}
#override double get maxExtent => 300;
#override double get minExtent => 200;
#override bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}
It's all good; except, I need the "header" to be transparent, but having it translucent, causes the underneathed list-items to get revealed (as per the screenshot below).
So, how to "mask-out" the "list items" that are visible through the "translucent header"?
How about using CustomClipper for List itself? Because the list height is dynamic during scrolling, the clip height must be calculated dynamically. So I pass the clipHeight into the custom clipper.
To get the clipHeight, I use MediaQuery.of(context).size.height - header height. So I create another class to get this value.
...
body: CustomWidget (
child: ListView.builder(
...
class CustomWidget extends StatelessWidget {
final Widget child;
CustomWidget({this.child,Key key}):super(key:key);
#override
Widget build(BuildContext context) {
return ClipRect(
clipper: MyCustomClipper(clipHeight: MediaQuery.of(context).size.height-200),
child: child,
);
}
}
class MyCustomClipper extends CustomClipper<Rect>{
final double clipHeight;
MyCustomClipper({this.clipHeight});
#override
getClip(Size size) {
double top = math.max(size.height - clipHeight,0) ;
Rect rect = Rect.fromLTRB(0.0, top, size.width, size.height);
return rect;
}
#override
bool shouldReclip(CustomClipper oldClipper) {
return false;
}
}
Pinned SliverPersistentHeader works like "CSS position: absolute".
So your body widget doesn't know that something is upon it.
One of the option is to not to use the SliverPersistentHeader.
import 'package:flutter/material.dart';
import 'dart:math' as math;
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
ScrollController controller;
#override
void initState() {
currentHeight = _maxExtent;
controller = ScrollController();
controller.addListener(() {
_updateHeaderHeight();
});
super.initState();
}
_updateHeaderHeight() {
double offset = controller.offset;
if (offset <= _maxExtent - _minExtent) {
setState(() {
currentHeight = math.max(_maxExtent - offset, _minExtent);
});
}
}
double currentHeight;
final double _maxExtent = 300;
final double _minExtent = 200;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: DecoratedBox(
// only to prove transparency
decoration: BoxDecoration(
image: DecorationImage(
colorFilter: ColorFilter.mode(Colors.white, BlendMode.color),
image: NetworkImage(
'https://picsum.photos/720/1280',
),
fit: BoxFit.cover,
),
),
child: Stack(
children: [
Header(currentHeight: currentHeight),
Padding(
padding: EdgeInsets.only(top: currentHeight),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.blueAccent),
),
child: ListView.builder(
controller: controller,
itemBuilder: (context, index) {
return ListTile(
title: Container(
color: Colors.amber.withOpacity(0.3),
child: Text('Item $index'),
),
);
},
),
),
),
],
),
),
),
);
}
}
class Header extends StatelessWidget {
const Header({Key key, this.currentHeight}) : super(key: key);
final double currentHeight;
#override
Widget build(BuildContext context) {
return Container(
height: currentHeight,
color: Colors.blue.withOpacity(0.75),
child: Placeholder(),
);
}
}

Is it possible to have any widget like this in flutter

Is there any built in widget in flutter to implement a widget like(red border in picture below) ?
which we can drag it change the value.
That's simply a horizontal ListView with predefined height.
SizedBox(
height: 56, // height of ListView
child: ListView.builder(
scrollDirection: Axis.horizontal, // makes it scroll in horizontal direction
itemBuilder: (_, index) {
// build your years
},
),
)
The desired output can be brought using the ListView.
Flutter just uploaded a video on ListView in which you can find your answer:
https://www.youtube.com/watch?v=KJpkjHGiI5A
Example code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
bool change ;
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int i=0;
void fxn(){
setState(() {
i++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('List View'),),
body: ListView.builder(
itemCount: 30,
itemBuilder: (_, index)=>Padding(
padding: const EdgeInsets.fromLTRB(0, 270, 0, 270),
child: MaterialButton(
child:
Text('200$index',style: TextStyle(fontSize: 25),),
onPressed: (){
},
color: Colors.white70,
),
),
scrollDirection: Axis.horizontal,
)
);
}
}
Output: Output

Allow GridView to overlap SliverAppBar

I am trying to reproduce the following example from the earlier Material design specifications (open for animated demo):
Until now I was able to produce the scrolling effect, but the overlap of the content is still missing. I couldn't find out how to do this properly.
import 'package:flutter/material.dart';
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
title: Text('Title'),
expandedHeight: 200.0,
primary: true,
pinned: true,
),
SliverFixedExtentList(
itemExtent: 30.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int i) => Text('Item $i')
),
),
],
),
);
}
}
I managed to get this functionality, using the ScrollController and a couple of tricks:
Here's the code:
ScrollController _scrollController;
static const kHeaderHeight = 235.0;
double get _headerOffset {
if (_scrollController.hasClients) if (_scrollController.offset > kHeaderHeight)
return -1 * (kHeaderHeight + 50.0);
else
return -1 * (_scrollController.offset * 1.5);
return 0.0;
}
#override
void initState() {
super.initState();
_scrollController = ScrollController()..addListener(() => setState(() {}));
}
#override
Widget build(BuildContext context) {
super.build(context);
return StackWithAllChildrenReceiveEvents(
alignment: AlignmentDirectional.topCenter,
children: [
Positioned(
top: _headerOffset,
child: Container(
height: kHeaderHeight,
width: MediaQuery.of(context).size.width,
color: Colors.blue,
),
),
Padding(
padding: EdgeInsets.only(left: 20.0, right: 20.0),
child: Feed(controller: _scrollController, headerHeight: kHeaderHeight),
),
],
);
}
To make the Feed() not overlap the blue container, I simply made the first child of it a SizedBox with the required height property.
Note that I am using a modified Stack class. That is in order to let the first Widget in the stack (the blue container) to detect presses, so it will fit my uses; unfortunately at this point the default Stack widget has an issue with that, you can read more about it over https://github.com/flutter/flutter/issues/18450.
The StackWithAllChildrenReceiveEvents code can be found over https://github.com/flutter/flutter/issues/18450#issuecomment-575447316.
I had the same problem and could not solve it with slivers. This example from another stackoverflow question solved my problem.
flutter - App bar scrolling with overlapping content in Flexible space
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Scroll demo',
home: new Scaffold(
appBar: new AppBar(elevation: 0.0),
body: new CustomScroll(),
),
);
}
}
class CustomScroll extends StatefulWidget {
#override
State createState() => new CustomScrollState();
}
class CustomScrollState extends State<CustomScroll> {
ScrollController scrollController;
double offset = 0.0;
static const double kEffectHeight = 100.0;
#override
Widget build(BuildContext context) {
return new Stack(
alignment: AlignmentDirectional.topCenter,
children: <Widget> [
new Container(
color: Colors.blue,
height: (kEffectHeight - offset * 0.5).clamp(0.0, kEffectHeight),
),
new Positioned(
child: new Container(
width: 200.0,
child: new ListView.builder(
itemCount: 100,
itemBuilder: buildListItem,
controller: scrollController,
),
),
),
],
);
}
Widget buildListItem(BuildContext context, int index) {
return new Container(
color: Colors.white,
child: new Text('Item $index')
);
}
void updateOffset() {
setState(() {
offset = scrollController.offset;
});
}
#override
void initState() {
super.initState();
scrollController = new ScrollController();
scrollController.addListener(updateOffset);
}
#override
void dispose() {
super.dispose();
scrollController.removeListener(updateOffset);
}
}
Change the list to a grid and its what you want