Flutter:如何根据 child 高度自动更新 AppBar 底部高度?
Flutter: How to automatically update AppBar bottom height depending on child height?
AppBar
s bottom 属性 是 PreferredSizeWidget
类型,所以我提供 PreferredSize
widget 需要在 preferredSize
属性 指定实际要使用的大小。但我不需要静态数字,而是根据 child 自动计算,在我的例子中是 Wrap
小部件。问题是 Wrap
的高度只有在使用 RenderObject
.
调用 build
方法后才能确定
我有:
我要的(gif编辑软件制作):
代码:
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
的主体
更新代码:
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,
];
}
而不是使用 bottomAppbar 属性,您应该创建一个列并添加两个子项,其中一个用于模拟 bottom appBar,另一个用于 实际主体并在其中添加wrap
小部件,
这样您可以获得完全相同的小部件结构,而不会妨碍 Appbar 小部件内的文本。
希望这个解决方案的观点可以帮助你。
AppBar
s bottom 属性 是 PreferredSizeWidget
类型,所以我提供 PreferredSize
widget 需要在 preferredSize
属性 指定实际要使用的大小。但我不需要静态数字,而是根据 child 自动计算,在我的例子中是 Wrap
小部件。问题是 Wrap
的高度只有在使用 RenderObject
.
build
方法后才能确定
我有:
我要的(gif编辑软件制作):
代码:
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
的主体
更新代码:
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,
];
}
而不是使用 bottomAppbar 属性,您应该创建一个列并添加两个子项,其中一个用于模拟 bottom appBar,另一个用于 实际主体并在其中添加wrap
小部件,
这样您可以获得完全相同的小部件结构,而不会妨碍 Appbar 小部件内的文本。
希望这个解决方案的观点可以帮助你。