だから私はしばらくフラッターを学んでいて、これで立ち往生しています。それがおかしな質問なら申し訳ありません。私は現在、カードタブのようなものを作成しようとしています。情報とウィジェットはカードに保存されます。
複数のカードスタックがあり、左右にスワイプしてナビゲートするTinderのようなものを想像してみてください。
それを作成する予定ですが、ボタンで新しいカードを追加/レンダリングする方法が見つからないようです。
これは、リストに何かを追加するようなものです。Flutterは、リストに追加するListViewビルダーを使用します。しかし、TabBarViewビルダーはありません。これは不可能なことですか?タブ内にリストを入れてみましたが、それでも同じではありません。
ここで、自分の意味を伝えるための基本的なスケルトンを作成しました。そのため、カードは左右にスワイプされ、appBarにカードを追加するためのボタンがあります。長さは2になり、ボタンで3枚目のカードをレンダリングしたかったのです。これは可能ですか?
前もって感謝します!
import 'package:flutter/material.Dart';
void main() {
runApp(new MaterialApp(
home: new CardStack(),
));
}
class CardStack extends StatefulWidget {
@override
_MainState createState() => new _MainState();
}
class _MainState extends State<CardStack> with SingleTickerProviderStateMixin {
TabController _cardController;
@override
void initState() {
super.initState();
_cardController = new TabController(vsync: this, length: 2);
}
@override
void dispose() {
_cardController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.grey[300],
appBar: new AppBar(
actions: <Widget>[
new IconButton(
icon: const Icon(Icons.add),
tooltip: 'Add Tabs',
onPressed: null,
),
],
title: new Text("Title Here"),
bottom: new PreferredSize(
preferredSize: const Size.fromHeight(20.0),
child: new Theme(
data: Theme.of(context).copyWith(accentColor: Colors.grey),
child: new Container(
height: 50.0,
alignment: Alignment.center,
child: new TabPageSelector(controller: _cardController),
),
)
)
),
body: new TabBarView(
controller: _cardController,
children: <Widget>[
new Center(
child: new Card(
child: new Container(
height: 450.0,
width: 300.0,
child: new IconButton(
icon: new Icon(Icons.favorite, size: 100.0),
tooltip: 'Favorited',
onPressed: null,
)
),
),
),
new Center(
child: new Card(
child: new Container(
height: 450.0,
width: 300.0,
child: new IconButton(
icon: new Icon(Icons.local_pizza, size: 50.0,),
tooltip: 'Pizza',
onPressed: null,
)
),
),
),
],
),
);
}
}
動的タブを作成するには、リストを使用して、ボタンをクリックするたびにリストを追加し続けることができます。
トリック:リストをクリアして空のウィジェットを再描画し、リストに従ってウィジェットを再度描画します。
import 'package:flutter/material.Dart';
void main() {
runApp(new MaterialApp(
home: new CardStack(),
));
}
class DynamicTabContent {
IconData icon;
String tooTip;
DynamicTabContent.name(this.icon, this.tooTip);
}
class CardStack extends StatefulWidget {
@override
_MainState createState() => new _MainState();
}
class _MainState extends State<CardStack> with TickerProviderStateMixin {
List<DynamicTabContent> myList = new List();
TabController _cardController;
TabPageSelector _tabPageSelector;
@override
void initState() {
super.initState();
myList.add(new DynamicTabContent.name(Icons.favorite, "Favorited"));
myList.add(new DynamicTabContent.name(Icons.local_pizza, "local pizza"));
_cardController = new TabController(vsync: this, length: myList.length);
_tabPageSelector = new TabPageSelector(controller: _cardController);
}
@override
void dispose() {
_cardController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.grey[300],
appBar: new AppBar(
actions: <Widget>[
new Padding(
padding: const EdgeInsets.all(1.0),
child: new IconButton(
icon: const Icon(
Icons.add,
size: 30.0,
color: Colors.white,
),
tooltip: 'Add Tabs',
onPressed: () {
List<DynamicTabContent> tempList = new List();
myList.forEach((dynamicContent) {
tempList.add(dynamicContent);
});
setState(() {
myList.clear();
});
if (tempList.length % 2 == 0) {
myList.add(new DynamicTabContent.name(Icons.shopping_cart, "shopping cart"));
} else {
myList.add(new DynamicTabContent.name(Icons.camera, "camera"));
}
tempList.forEach((dynamicContent) {
myList.add(dynamicContent);
});
setState(() {
_cardController = new TabController(vsync: this, length: myList.length);
_tabPageSelector = new TabPageSelector(controller: _cardController);
});
},
),
),
],
title: new Text("Title Here"),
bottom: new PreferredSize(
preferredSize: const Size.fromHeight(10.0),
child: new Theme(
data: Theme.of(context).copyWith(accentColor: Colors.grey),
child: myList.isEmpty
? new Container(
height: 30.0,
)
: new Container(
height: 30.0,
alignment: Alignment.center,
child: _tabPageSelector,
),
))),
body: new TabBarView(
controller: _cardController,
children: myList.isEmpty
? <Widget>[]
: myList.map((dynamicContent) {
return new Card(
child: new Container(
height: 450.0,
width: 300.0,
child: new IconButton(
icon: new Icon(dynamicContent.icon, size: 100.0),
tooltip: dynamicContent.tooTip,
onPressed: null,
)),
);
}).toList(),
),
);
}
}
お役に立てれば :)
アレイを変更する必要がある場合、問題が発生します。それらは、アレイを変更するときに同じコントローラーを使用する機会がないという事実にあります。
この場合、次のカスタムウィジェットを使用できます。
import 'package:flutter/material.Dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> data = ['Page 0', 'Page 1', 'Page 2'];
int initPosition = 1;
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: CustomTabView(
initPosition: initPosition,
itemCount: data.length,
tabBuilder: (context, index) => Tab(text: data[index]),
pageBuilder: (context, index) => Center(child: Text(data[index])),
onPositionChange: (index){
print('current position: $index');
initPosition = index;
},
onScroll: (position) => print('$position'),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
data.add('Page ${data.length}');
});
},
child: Icon(Icons.add),
),
);
}
}
/// Implementation
class CustomTabView extends StatefulWidget {
final int itemCount;
final IndexedWidgetBuilder tabBuilder;
final IndexedWidgetBuilder pageBuilder;
final Widget stub;
final ValueChanged<int> onPositionChange;
final ValueChanged<double> onScroll;
final int initPosition;
CustomTabView({
@required this.itemCount,
@required this.tabBuilder,
@required this.pageBuilder,
this.stub,
this.onPositionChange,
this.onScroll,
this.initPosition,
});
@override
_CustomTabsState createState() => _CustomTabsState();
}
class _CustomTabsState extends State<CustomTabView> with TickerProviderStateMixin {
TabController controller;
int _currentCount;
int _currentPosition;
@override
void initState() {
_currentPosition = widget.initPosition ?? 0;
controller = TabController(
length: widget.itemCount,
vsync: this,
initialIndex: _currentPosition,
);
controller.addListener(onPositionChange);
controller.animation.addListener(onScroll);
_currentCount = widget.itemCount;
super.initState();
}
@override
void didUpdateWidget(CustomTabView oldWidget) {
if (_currentCount != widget.itemCount) {
controller.animation.removeListener(onScroll);
controller.removeListener(onPositionChange);
controller.dispose();
if (widget.initPosition != null) {
_currentPosition = widget.initPosition;
}
if (_currentPosition > widget.itemCount - 1) {
_currentPosition = widget.itemCount - 1;
_currentPosition = _currentPosition < 0 ? 0 :
_currentPosition;
if (widget.onPositionChange is ValueChanged<int>) {
WidgetsBinding.instance.addPostFrameCallback((_){
if(mounted) {
widget.onPositionChange(_currentPosition);
}
});
}
}
_currentCount = widget.itemCount;
setState(() {
controller = TabController(
length: widget.itemCount,
vsync: this,
initialIndex: _currentPosition,
);
controller.addListener(onPositionChange);
controller.animation.addListener(onScroll);
});
} else if (widget.initPosition != null) {
controller.animateTo(widget.initPosition);
}
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
controller.animation.removeListener(onScroll);
controller.removeListener(onPositionChange);
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (widget.itemCount < 1) return widget.stub ?? Container();
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
alignment: Alignment.center,
child: TabBar(
isScrollable: true,
controller: controller,
labelColor: Theme.of(context).primaryColor,
unselectedLabelColor: Theme.of(context).hintColor,
indicator: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context).primaryColor,
width: 2,
),
),
),
tabs: List.generate(
widget.itemCount,
(index) => widget.tabBuilder(context, index),
),
),
),
Expanded(
child: TabBarView(
controller: controller,
children: List.generate(
widget.itemCount,
(index) => widget.pageBuilder(context, index),
),
),
),
],
);
}
onPositionChange() {
if (!controller.indexIsChanging) {
_currentPosition = controller.index;
if (widget.onPositionChange is ValueChanged<int>) {
widget.onPositionChange(_currentPosition);
}
}
}
onScroll() {
if (widget.onScroll is ValueChanged<double>) {
widget.onScroll(controller.animation.value);
}
}
}