ScrollExtent is negative in flutter - flutter-layout

Getting a very pesky error on this segment of my Flutter app and no clue why:
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1,
childAspectRatio: 4 / 3,
mainAxisSpacing: 30.0,
crossAxisSpacing: 20.0),
padding: EdgeInsets.only(left: 20),
scrollDirection: Axis.horizontal,
itemCount: products.length,
itemBuilder: (context, i) => ChangeNotifierProvider.value(
value: products[i],
child: Consumer<Product>(
builder: (context, product, _) {
return ProductCard(
product: product,
onSelected: (prod) {
setState(() {
products.forEach(
(item) {
item.isSelected = false;
},
);
prod.isSelected = true;
});
here's the error: SliverGeometry is not valid: The "scrollExtent" is negative.geometry: SliverGeometry(scrollExtent: -10.0, paintExtent: 20.0, maxPaintExtent: -10.0, cacheExtent: 20.0)
scrollExtent: -10.0
paintExtent: 20.0
maxPaintExtent: -10.0
cacheExtent: 20.0
padding: EdgeInsets(20.0, 0.0, 0.0, 0.0)
This error is preventing emulation on Android even though the apk is installed but it just won't run. It runs on iOS but the error remains.

Pretty sure you've sorted it out already but as I don't see any accepted answer I'll leave mine to be useful to others.
I was in the same situation and I had the same problem, dough in the cross axis.
The problem is that you're providing both mainAxisSpacing: 30.0 and crossAxisSpacing: 20.0 values.
With a crossAxisCount: 1 you're not using the main axis so mainAxisSpacing throws the infamous SliverGeometry is not valid: The "scrollExtent" is negative.error.
Independently from your scrollingDirection you should only pass the crossAxisSpacing value.
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1,
childAspectRatio: 4 / 3,
crossAxisSpacing: 20.0),
padding: EdgeInsets.only(left: 20),
scrollDirection: Axis.horizontal,
itemCount: products.length,
itemBuilder: (context, i) => ChangeNotifierProvider.value(
value: products[i],
child: Consumer<Product>(
builder: (context, product, _) {
return ProductCard(
product: product,
onSelected: (prod) {
setState(() {
products.forEach(
(item) {
item.isSelected = false;
},
);
prod.isSelected = true;
});

It is my code that was causing the same, Exception
before exception
I searched a lot, in short, it is how i solve it
What i did to fix it
In my case, changing gridDelegate, my exception is fixed!
Note: Don't use any Mathematical operation like (/,*) that may produce a double value!!!
Give a like if you find it Useful! :)

I found the issue,
I was using empty list in gridview builder
List<int> myList = [];
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisSpacing: Dimensions.PADDING_SIZE_LARGE,
mainAxisSpacing: ResponsiveHelper.isDesktop(context)
? Dimensions.PADDING_SIZE_LARGE
: 0.01,
childAspectRatio:
ResponsiveHelper.isDesktop(context) ? 4 : 4,
crossAxisCount:
ResponsiveHelper.isMobile(context) ? 1 : 2,
),
physics: BouncingScrollPhysics(),
shrinkWrap: true,
itemCount: myList.length,
itemBuilder: (context, index) {
return Text("");
},
)
myList was not having any values

Related

How to split a String in Dart, Flutter

I am getting a String balance from the backend, and it is something like this 430000.
I would like to have an output like this 430,000.
How do I go about it?
Row(
children: [
Padding(
padding: const EdgeInsets.only(
left: 12,
),
child: Text(
"₦ ${user.availableBalance.toStringAsFixed(0)} ",
style: TextStyle(
color: Colors.white,
fontSize: 21.sp,
),
)),
],
),
The best way is to use Regex so define this on the top
class _HomeTabState extends State<HomeTab> {
//Regex
RegExp reg = RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'); //immport dar:core
RegExp regex = RegExp(r'([.]*0)(?!.*\d)');
String Function(Match) mathFunc = (Match match) => '${match[1]},'; //match
...
//In your build
Widget build(BuildContext context) {
...
Row(
children: [
Padding(
padding: const EdgeInsets.only(
left: 12,
),
child: Text(
"₦ ${double.parse(user.availableBalance.toString()).toStringAsFixed(0)} " .replaceAllMapped(
reg, mathFunc),
style: TextStyle(
color: Colors.white,
fontSize: 21.sp,
),
)),
],
),
...
}
}
Then to use it just turn your balance into a double then to String in order to access the toStringAsFixed(0) like this
"${double.parse(balance.toString()).toStringAsFixed(0)}".replaceAllMapped(reg, mathFunc),
so for 430 -> would be 430 and 4300 -> would be 4,300 and 43000 -> would be 43,000
I would suggest using the intl package https://pub.dev/packages/intl
Example usage:
var f = NumberFormat("#,###.##");
print(f.format(123456789.4567));
This will result in the following output:
123,456,789.46
Note that , in the pattern specifies the grouping separator and ### specifies the group size to be 3. .##specifies rounding on two decimal points.
Check for additional documentation: https://api.flutter.dev/flutter/intl/NumberFormat-class.html

How to make flutter animation play full-screen when modal bottom sheet has been raised?

I am trying to add a confetti animation to modal bottom sheet using confetti package when it is raised. But it is not working as intended - animation is being cut in the middle of the screen. It works as intended when I call the animation from button press. Desired result is like on button press, but should be when modal bottom sheet is raised.
I have tried wrapping ConfettiWidget inside Expanded, Positioned.fill, also Flexible, but these don't work.
EDIT:
Did some testing and it seems that the problem is present only on iOS devices. Also, it probably is worth mentioning that the animation is way slower on Android device (both physical and virtual) than it is on an iPhone.
See gifs below:
On Build:
On button press:
(How it should look on the build)
Code:
#override
void initState() {
confController = ConfettiController(duration: Duration(seconds: 5));
WidgetsBinding.instance!.addPostFrameCallback((_) {
confController.play();
});
super.initState();
}
#override
void dispose() {
confController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Container(
color: Colors.black,
child: Stack(
children: [
Align(
alignment: Alignment.topCenter,
child: ConfettiWidget(
confettiController: confController,
blastDirectionality: BlastDirectionality.explosive,
particleDrag: 0.05,
emissionFrequency: 0.1,
gravity: 0.3,
colors: [
Colors.red,
], // manually specify the colors to be used
),
),
Container(
width: 500,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RawMaterialButton(
onPressed: () {
confController.play();
},
fillColor: Colors.white,
)
],
),
)
],
),
);
}
Needed to set the ConfettiWidget parameter canvas to MediaQuery.of(context).size * 2. Works as intended now (tested on both smaller and bigger devices (biggest was iPad 12.9-inch)). Code looks like this now:
ConfettiWidget(
confettiController: confController,
blastDirectionality: BlastDirectionality.explosive,
particleDrag: 0.05,
emissionFrequency: 0.1,
gravity: 0.3,
canvas: MediaQuery.of(context).size * 2,
colors: [
Colors.red,
],
)

Flutter screenshot without using RepaintBoundary

I am working on a drawing app. I can get a screenshot of a widget using RepaintBoundary but I need to update it with a button which is outside. In my case, I can clear the list of points with a FlatButton but my Container is not clearing because of RepaintBoundary I need to get a screenshot of my Container without RepaintBoundary or I should update my Container from outside they both works for me. Thanks.
return
SingleChildScrollView(//physics: NeverScrollableScrollPhysics(),
child: Column(
children: <Widget>[
RepaintBoundary(
key: _signKey,
child: new Container(
width: 100.0 * MediaQuery.of(context).devicePixelRatio ,
height: 150.0 * MediaQuery.of(context).devicePixelRatio,
color: Colors.black12,
child: GestureDetector(
onPanUpdate: (DragUpdateDetails details) {
setState(() {
RenderBox object = context.findRenderObject();
Offset _localPosition =
object.globalToLocal(details.globalPosition);
points = new List.from(points)
..add(_localPosition);
});
},
onPanEnd: (DragEndDetails details) => points.add(null),
child: new CustomPaint(
painter: new DrawingPainter(points: points),
size: Size.infinite,
),
),
),
),
Row(
children: <Widget>[
FlatButton( //Button to clear container
padding: EdgeInsets.fromLTRB(20, 10, 20, 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
color: Colors.redAccent,
textColor: Colors.black,
onPressed: (){
setState(() {
points.clear();
});
},
child: Text('Sil'),
),
],
),
],
),
);

How do I define RadioListTitles horizontally?

can you help me with my problem? My app have 2 RadioListTitle, they are located vertically, but I need to locate them horizontally. How to do it?
RadioListTile(
title: const Text('Мужской'),
value: GenderList.male,
groupValue: _gender,
onChanged: (GenderList value) {setState(() { _gender = value;});},
),
RadioListTile(
title: const Text('Женский'),
value: GenderList.female,
groupValue: _gender,
onChanged: (GenderList value) {setState(() { _gender = value;});},
),
Wrap it with Row and Expanded
Row(
children:[
Expanded(child: RadioListTile(...) ),
Expanded(child: RadioListTile(...) ),
],
)

To-Do List: CheckBox activating every member of list

I started creating a To-Do List App with Flutter.
And so far, I've added an AppBar, FloatingActionButton as adding button, List, TextField...
But I have a problem with the CheckBox IconButton. When its pressed it activates every member of the list instead only one.
Any solutions?
1 ListView.builder(
2 itemCount: todo.length,
3 itemBuilder: (context, index) {
4 return Card(
5 elevation: 5.0,
6 shape: RoundedRectangleBorder(
7 borderRadius: BorderRadius.circular(8),
8 ),
9 margin: EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 4.0),
10 child: ListTile(
11 title: Text(
12 todo[index],
13 style: TextStyle(
14 color: (isPressed) ? Colors.grey[700] : Colors.black,
15 decoration: (isPressed) ? TextDecoration.lineThrough : TextDecoration.none
16 ),
17 ),
18 trailing:
21 IconButton(
22 icon: Icon(
23 Icons.check_box,
24 color: (isPressed) ? Colors.blueAccent : Colors.grey,
25 ),
26 onPressed: () {
27 setState(() {
28 isPressed = !isPressed;
29 });
30 },
31 )
At line 15. I added when the button is pressed a.k.a activated the member of the list to be "done" by having a line through and its colors from black to go grey. But, when I activate the button that happens to all members of the list.
As I see, isPressed is used for all of your listview items while each item should has its own isPressed value. You can create a list of booleans and assign each one of them to a list item, something like this:
List<bool> _radioList=[false,...]; //its length should be equal to todo.length
And then in your list you can use:
color: (_radioList[index]) ? Colors.grey[700] : Colors.black,
decoration: (_radioList[index]) ? TextDecoration.lineThrough : TextDecoration.none
..
color: (_radioList[index]) ? Colors.blueAccent : Colors.grey,
..
onPressed: () {
setState(() {
_radioList[index] = !_radioList[index];
});

Resources