Loïc Sharma d248a651b6
Add flutter_lints to samples (#179091)
I recommend reviewing each commit individually.

The following were suppressed instead of migrated to minimize the time
the tree is closed:

1. The [`Radio` -> `RadioGroup`
migration](https://docs.flutter.dev/release/breaking-changes/radio-api-redesign).
Tracked by: https://github.com/flutter/flutter/issues/179088.

Part of: https://github.com/flutter/flutter/issues/178827

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
2025-11-26 22:11:20 +00:00

196 lines
7.3 KiB
Dart

// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(const GridViewExampleApp());
class GridViewExampleApp extends StatelessWidget {
const GridViewExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Padding(
padding: const EdgeInsets.all(20.0),
child: Card(
elevation: 8.0,
child: GridView.builder(
padding: const EdgeInsets.all(12.0),
gridDelegate: CustomGridDelegate(dimension: 240.0),
// Try uncommenting some of these properties to see the effect on the grid:
// itemCount: 20, // The default is that the number of grid tiles is infinite.
// scrollDirection: Axis.horizontal, // The default is vertical.
// reverse: true, // The default is false, going down (or left to right).
itemBuilder: (BuildContext context, int index) {
final math.Random random = math.Random(index);
return GridTile(
header: GridTileBar(
title: Text(
'$index',
style: const TextStyle(color: Colors.black),
),
),
child: Container(
margin: const EdgeInsets.all(12.0),
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
gradient: const RadialGradient(
colors: <Color>[Color(0x0F88EEFF), Color(0x2F0099BB)],
),
),
child: FlutterLogo(
style: FlutterLogoStyle
.values[random.nextInt(FlutterLogoStyle.values.length)],
),
),
);
},
),
),
),
);
}
}
class CustomGridDelegate extends SliverGridDelegate {
CustomGridDelegate({required this.dimension});
// This is the desired height of each row (and width of each square).
// When there is not enough room, we shrink this to the width of the scroll view.
final double dimension;
// The layout is two rows of squares, then one very wide cell, repeat.
@override
SliverGridLayout getLayout(SliverConstraints constraints) {
// Determine how many squares we can fit per row.
int count = constraints.crossAxisExtent ~/ dimension;
if (count < 1) {
count = 1; // Always fit at least one regardless.
}
final double squareDimension = constraints.crossAxisExtent / count;
return CustomGridLayout(
crossAxisCount: count,
fullRowPeriod:
3, // Number of rows per block (one of which is the full row).
dimension: squareDimension,
);
}
@override
bool shouldRelayout(CustomGridDelegate oldDelegate) {
return dimension != oldDelegate.dimension;
}
}
class CustomGridLayout extends SliverGridLayout {
const CustomGridLayout({
required this.crossAxisCount,
required this.dimension,
required this.fullRowPeriod,
}) : assert(crossAxisCount > 0),
assert(fullRowPeriod > 1),
loopLength = crossAxisCount * (fullRowPeriod - 1) + 1,
loopHeight = fullRowPeriod * dimension;
final int crossAxisCount;
final double dimension;
final int fullRowPeriod;
// Computed values.
final int loopLength;
final double loopHeight;
@override
double computeMaxScrollOffset(int childCount) {
// This returns the scroll offset of the end side of the childCount'th child.
// In the case of this example, this method is not used, since the grid is
// infinite. However, if one set an itemCount on the GridView above, this
// function would be used to determine how far to allow the user to scroll.
if (childCount == 0 || dimension == 0) {
return 0;
}
return (childCount ~/ loopLength) * loopHeight +
((childCount % loopLength) ~/ crossAxisCount) * dimension;
}
@override
SliverGridGeometry getGeometryForChildIndex(int index) {
// This returns the position of the index'th tile.
//
// The SliverGridGeometry object returned from this method has four
// properties. For a grid that scrolls down, as in this example, the four
// properties are equivalent to x,y,width,height. However, since the
// GridView is direction agnostic, the names used for SliverGridGeometry are
// also direction-agnostic.
//
// Try changing the scrollDirection and reverse properties on the GridView
// to see how this algorithm works in any direction (and why, therefore, the
// names are direction-agnostic).
final int loop = index ~/ loopLength;
final int loopIndex = index % loopLength;
if (loopIndex == loopLength - 1) {
// Full width case.
return SliverGridGeometry(
scrollOffset: (loop + 1) * loopHeight - dimension, // "y"
crossAxisOffset: 0, // "x"
mainAxisExtent: dimension, // "height"
crossAxisExtent: crossAxisCount * dimension, // "width"
);
}
// Square case.
final int rowIndex = loopIndex ~/ crossAxisCount;
final int columnIndex = loopIndex % crossAxisCount;
return SliverGridGeometry(
scrollOffset: (loop * loopHeight) + (rowIndex * dimension), // "y"
crossAxisOffset: columnIndex * dimension, // "x"
mainAxisExtent: dimension, // "height"
crossAxisExtent: dimension, // "width"
);
}
@override
int getMinChildIndexForScrollOffset(double scrollOffset) {
// This returns the first index that is visible for a given scrollOffset.
//
// The GridView only asks for the geometry of children that are visible
// between the scroll offset passed to getMinChildIndexForScrollOffset and
// the scroll offset passed to getMaxChildIndexForScrollOffset.
//
// It is the responsibility of the SliverGridLayout to ensure that
// getGeometryForChildIndex is consistent with getMinChildIndexForScrollOffset
// and getMaxChildIndexForScrollOffset.
//
// Not every child between the minimum child index and the maximum child
// index need be visible (some may have scroll offsets that are outside the
// view; this happens commonly when the grid view places tiles out of
// order). However, doing this means the grid view is less efficient, as it
// will do work for children that are not visible. It is preferred that the
// children are returned in the order that they are laid out.
final int rows = scrollOffset ~/ dimension;
final int loops = rows ~/ fullRowPeriod;
final int extra = rows % fullRowPeriod;
return loops * loopLength + extra * crossAxisCount;
}
@override
int getMaxChildIndexForScrollOffset(double scrollOffset) {
// (See commentary above.)
final int rows = scrollOffset ~/ dimension;
final int loops = rows ~/ fullRowPeriod;
final int extra = rows % fullRowPeriod;
final int count = loops * loopLength + extra * crossAxisCount;
if (extra == fullRowPeriod - 1) {
return count;
}
return count + crossAxisCount - 1;
}
}