Closed liudonghua123 closed 4 years ago
i don't think they are issues, you should understand more about silver. NestedScrollView(body:Column(tabbar,Expaned(tabBarView)) will be ok for you
@zmtzawqlp hi, Thanks your tips, I tried your suggestions and write the following demonstrate code, but it still not work. You can quickly test it in https://dartpad.dev/ or https://dartpad.cn/.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SamplePage(),
),
);
}
}
class SamplePage extends StatefulWidget {
SamplePage({Key key}) : super(key: key);
@override
_SamplePageState createState() => _SamplePageState();
}
class _SamplePageState extends State<SamplePage> with TickerProviderStateMixin {
TabController _tabController;
var tabData = ['tab1', 'tab2'];
@override
void initState() {
_tabController = TabController(length: tabData.length, vsync: this);
_tabController.addListener(() {
if (_tabController.indexIsChanging) {
setState(() {});
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
return NestedScrollView(
headerSliverBuilder: (context, value) {
return [];
},
body: Column(
children: [
/// some widget above tab header
Container(
height: 200,
alignment: Alignment.center,
child: Text('Header'),
),
/// tab header
TabBar(
controller: _tabController,
tabs: [
...tabData.map(
(item) => Tab(
child: Text(item),
),
),
],
),
/// tab content
Expanded(
child: TabBarView(
controller: _tabController,
children: <Widget>[
tabContent('tab1', 50),
tabContent('tab2', 5),
],
),
),
/// some widget below tab content
Container(
height: 100,
alignment: Alignment.center,
child: Text('Footer'),
),
],
),
);
}
Widget tabContent(String title, int length) {
return Column(
children: [
...List.generate(
length,
(index) => ListTile(
title: Text('${title} item ${index}'),
),
),
],
);
}
}
I have also tried the following code initial posted in stackoverflow (https://stackoverflow.com/questions/54642710/tabbarview-with-dynamic-container-height/57383014#57383014), but it still have some issue about the height and add header/footer around the tabbarview.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: MyHomePage(),
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
final bodyGlobalKey = GlobalKey();
final List<Widget> myTabs = [
Tab(text: 'auto short'),
Tab(text: 'auto long'),
Tab(text: 'fixed'),
];
TabController _tabController;
ScrollController _scrollController;
bool fixedScroll;
Widget _buildCarousel() {
return Stack(
children: <Widget>[
Placeholder(fallbackHeight: 100),
Positioned.fill(
child: Align(alignment: Alignment.center, child: Text('Slider'))),
],
);
}
@override
void initState() {
_scrollController = ScrollController();
_tabController = TabController(length: 3, vsync: this);
_tabController.addListener(_smoothScrollToTop);
super.initState();
}
@override
void dispose() {
_tabController.dispose();
_scrollController.dispose();
super.dispose();
}
_smoothScrollToTop() {
_scrollController.animateTo(
0,
duration: Duration(microseconds: 300),
curve: Curves.ease,
);
}
_buildTabContext(int lineCount) => Container(
child: ListView.builder(
physics: const ClampingScrollPhysics(),
itemCount: lineCount,
itemBuilder: (BuildContext context, int index) {
return Text('some content');
},
),
);
@override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (context, value) {
return [
SliverToBoxAdapter(child: _buildCarousel()),
SliverToBoxAdapter(
child: TabBar(
controller: _tabController,
labelColor: Colors.redAccent,
isScrollable: true,
tabs: myTabs,
),
),
];
},
body: Container(
child: TabBarView(
controller: _tabController,
children: [
_buildTabContext(50),
_buildTabContext(200),
_buildTabContext(2)
],
),
),
),
);
}
}
Finally, I tried using AnimatedSwitcher
with conditional rendering, and add some simulative tab page switch animation. But what is not perfect is the animation is not exact same as the TabBarView
, and miss the switch gesture on tab page content currently (I will read more docs about how to implement this). If the TabBarView could fit its children's height, then no such hacks need.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SamplePage(),
),
);
}
}
class SamplePage extends StatefulWidget {
SamplePage({Key key}) : super(key: key);
@override
_SamplePageState createState() => _SamplePageState();
}
class _SamplePageState extends State<SamplePage> with TickerProviderStateMixin {
TabController _tabController;
var tabData = ['tab1', 'tab2'];
@override
void initState() {
_tabController = TabController(length: tabData.length, vsync: this);
_tabController.addListener(() {
if (_tabController.indexIsChanging) {
setState(() {});
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
/// some widget above tab header
Container(
height: 200,
alignment: Alignment.center,
child: Text('Header'),
),
/// tab header
TabBar(
controller: _tabController,
tabs: [
...tabData.map(
(item) => Tab(
child: Text(item),
),
),
],
),
/// tab content
AnimatedSwitcher(
duration: Duration(milliseconds: 500),
switchInCurve: Curves.easeInOut,
switchOutCurve: Curves.easeInOut,
transitionBuilder: (Widget child, Animation<double> animation) {
var direction = _tabController.index > _tabController.previousIndex
? AxisDirection.left
: AxisDirection.right;
return SlideTransitionX(
child: child,
direction: direction,
position: animation,
);
},
child: _tabController.index == 0
? tabContent('tab1', 50)
: tabContent('tab2', 10),
),
],
));
}
Widget tabContent(
String title,
int length,
) {
return Column(
key: ValueKey<int>(length),
children: [
...List.generate(
length,
(index) => ListTile(
title: Text('${title} item ${index}'),
trailing: Icon(Icons.access_alarm),
),
),
],
);
}
}
/// code copied from https://book.flutterchina.club/chapter9/animated_switcher.html
class SlideTransitionX extends AnimatedWidget {
SlideTransitionX({
Key key,
@required Animation<double> position,
this.transformHitTests = true,
this.direction = AxisDirection.down,
this.child,
}) : assert(position != null),
super(key: key, listenable: position) {
// 偏移在内部处理
switch (direction) {
case AxisDirection.up:
_tween = Tween(begin: Offset(0, 1), end: Offset(0, 0));
break;
case AxisDirection.right:
_tween = Tween(begin: Offset(-1, 0), end: Offset(0, 0));
break;
case AxisDirection.down:
_tween = Tween(begin: Offset(0, -1), end: Offset(0, 0));
break;
case AxisDirection.left:
_tween = Tween(begin: Offset(1, 0), end: Offset(0, 0));
break;
}
}
Animation<double> get position => listenable;
final bool transformHitTests;
final Widget child;
//退场(出)方向
final AxisDirection direction;
Tween<Offset> _tween;
@override
Widget build(BuildContext context) {
Offset offset = _tween.evaluate(position);
if (position.status == AnimationStatus.reverse) {
switch (direction) {
case AxisDirection.up:
offset = Offset(offset.dx, -offset.dy);
break;
case AxisDirection.right:
offset = Offset(-offset.dx, offset.dy);
break;
case AxisDirection.down:
offset = Offset(offset.dx, -offset.dy);
break;
case AxisDirection.left:
offset = Offset(-offset.dx, offset.dy);
break;
}
}
return FractionalTranslation(
translation: offset,
transformHitTests: transformHitTests,
child: child,
);
}
}
加群问吧。完全不知道你的需求
There is some existed issue in
TabBarView
about theheight
. see https://github.com/flutter/flutter/issues/55464, https://github.com/flutter/flutter/issues/54968. Can we fix this inExtendedTabBarView
?