Свойство bottom
AppBar
s имеет тип PreferredSizeWidget
, поэтому я предоставляю виджет PreferredSize
, который требует в свойстве preferredSize
указать фактический размер для использования. Но мне нужно не stati c число, а автоматически рассчитывается в зависимости от ребенка, который в моем случае является Wrap
виджет. Проблема в том, что высоту Wrap
можно определить только после вызова метода build
с использованием RenderObject
.
Что у меня есть:
Что я хочу (сделано с помощью программного обеспечения gif-редактирования):
Код в DartPad
Код:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final items = List.generate(400, (index) => '$index');
final filterOptions = <IntType>[];
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: buildBody(),
appBar: AppBar(
title: Text('Simple filtering'),
bottom: buildAppBarBottom(),
),
),
);
}
Widget buildBody() {
return Scrollbar(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
if (filterOptions.every((option) => item.contains(option.symbol))) {
return ListTile(title: Text(item));
}
return Container(height: 0.0001);
},
),
);
}
PreferredSizeWidget buildAppBarBottom() {
return PreferredSize(
preferredSize: Size.fromHeight(100), // change height depending on the child height
child: Wrap(
spacing: 8,
children: IntType.values.map((option) {
return FilterChip(
selectedColor: Colors.white,
selected: filterOptions.contains(option),
onSelected: (isSelected) {
setState(() {
if (isSelected) {
filterOptions.add(option);
} else {
filterOptions.remove(option);
}
});
},
label: Text(option.name),
);
}).toList(),
),
);
}
}
class IntType {
static const IntType one = const IntType._('One', '1');
static const IntType two = const IntType._('Two', '2');
static const IntType three = const IntType._('Three', '3');
static const IntType four = const IntType._('Four', '4');
static const IntType five = const IntType._('Five', '5');
static const IntType six = const IntType._('Six', '6');
static const IntType seven = const IntType._('Seven', '7');
static const IntType eight = const IntType._('Eight', '8');
static const IntType nine = const IntType._('Nine', '9');
static const IntType zero = const IntType._('Zero', '0');
final String name;
final String symbol;
const IntType._(this.name, this.symbol);
static const values = [
IntType.one,
IntType.two,
IntType.three,
IntType.four,
IntType.five,
IntType.six,
IntType.seven,
IntType.eight,
IntType.nine,
IntType.zero,
];
}
РЕДАКТИРОВАТЬ:
Благодаря ответу AKS Обходной путь должен переместиться Wrap
в Scaffold
тело
Обновлен код в DartPad
Обновлен код:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final items = List.generate(400, (index) => '$index');
final filterOptions = <IntType>[];
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(appBarTheme: AppBarTheme(elevation: 0)),
home: Scaffold(
body: buildBody(),
appBar: AppBar(
title: Text('Simple filtering'),
),
),
);
}
Widget buildBody() {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Material(
elevation: 4,
child: Container(
color: Theme.of(context).primaryColor,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Wrap(
alignment: WrapAlignment.center,
spacing: 8,
children: IntType.values.map((option) {
return FilterChip(
selectedColor: Colors.white,
selected: filterOptions.contains(option),
onSelected: (isSelected) {
setState(() {
if (isSelected) {
filterOptions.add(option);
} else {
filterOptions.remove(option);
}
});
},
label: Text(option.name),
);
}).toList(),
),
),
),
),
Expanded(
child: Scrollbar(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
if (filterOptions
.every((option) => item.contains(option.symbol))) {
return ListTile(title: Text(item));
}
return Container(height: 0.0001);
},
),
),
),
],
);
}
}
class IntType {
static const IntType one = const IntType._('One', '1');
static const IntType two = const IntType._('Two', '2');
static const IntType three = const IntType._('Three', '3');
static const IntType four = const IntType._('Four', '4');
static const IntType five = const IntType._('Five', '5');
static const IntType six = const IntType._('Six', '6');
static const IntType seven = const IntType._('Seven', '7');
static const IntType eight = const IntType._('Eight', '8');
static const IntType nine = const IntType._('Nine', '9');
static const IntType zero = const IntType._('Zero', '0');
final String name;
final String symbol;
const IntType._(this.name, this.symbol);
static const values = [
IntType.one,
IntType.two,
IntType.three,
IntType.four,
IntType.five,
IntType.six,
IntType.seven,
IntType.eight,
IntType.nine,
IntType.zero,
];
}