タイトル状態と同様に、どのようにしてStatefulWidgetからStatefulWidgetの状態にアクセスできますか。
背景: 5つの "StarWidget"が連続して構成されている星評価ウィジェットがあります。 StarWidgetクラスは、検出器がラップされた単なるアイコンです(サイズが非常に大きいため、IconButtonを使用しません)。 StarWidgetは、それが選択されているかどうかを対応するStateオブジェクトに保存し、それに応じて塗りつぶしアイコンまたはアウトラインアイコンを表示します。
私のメインウィジェットでは、StatefulWidgetオブジェクトにアクセスでき、それらの状態を構成したいと考えています。
import 'package:flutter/material.Dart';
import 'package:font_awesome_flutter/font_awesome_flutter.Dart';
class StarRatingWidget extends StatefulWidget {
@override
_StarRatingWidgetState createState() {
return _StarRatingWidgetState();
}
}
class _StarRatingWidgetState extends State<StarRatingWidget>
implements StarSelectionInterface {
//Properties
int _currentRating = 0;
List<RatingStarWidget> starWidgets = [];
//Methods
@override
void initState() {
super.initState();
starWidgets.add(
RatingStarWidget(
starSelectionInterface: this,
starPosition: 0,
),
);
starWidgets.add(
RatingStarWidget(
starSelectionInterface: this,
starPosition: 1,
),
);
starWidgets.add(
RatingStarWidget(
starSelectionInterface: this,
starPosition: 2,
),
);
starWidgets.add(
RatingStarWidget(
starSelectionInterface: this,
starPosition: 3,
),
);
starWidgets.add(
RatingStarWidget(
starSelectionInterface: this,
starPosition: 4,
),
);
}
@override
Widget build(BuildContext buildContext) {
return Row(
children: starWidgets,
);
}
//Star Selection Interface Methods
void onStarSelected(_RatingStarWidgetState starWidgetState) {
print("listener: star selected ${starWidgetState._starPosition}");
//a new, rating has been selected, update rating
if (_currentRating != starWidgetState._starPosition) {
_currentRating = (starWidgetState._starPosition + 1);
}
//same star as rating has been selected, set rating to 0
else {
_currentRating = 0;
}
//update stars according to rating
for(int i = 1; i <= 5; i++) {
//what should I do here?!
}
}
}
class RatingStarWidget extends StatefulWidget {
//Properties
final int starPosition;
final StarSelectionInterface starSelectionInterface;
//Constructors
RatingStarWidget({this.starSelectionInterface, this.starPosition});
//Methods
@override
_RatingStarWidgetState createState() {
return _RatingStarWidgetState(starSelectionInterface, starPosition);
}
}
class _RatingStarWidgetState extends State<RatingStarWidget> {
//Properties
int _starPosition;
bool _isSelected = false;
StarSelectionInterface selectionListener;
//Constructors
_RatingStarWidgetState(this.selectionListener, this._starPosition);
//Methods
@override
Widget build(BuildContext buildContext) {
return AnimatedCrossFade(
firstChild: GestureDetector(
child: Icon(
FontAwesomeIcons.star,
size: 14,
),
onTap: () {
print("star: selected");
selectionListener.onStarSelected(this);
},
),
secondChild: GestureDetector(
child: Icon(
FontAwesomeIcons.solidStar,
size: 14,
),
onTap: () {
selectionListener.onStarSelected(this);
},
),
duration: Duration(milliseconds: 300),
crossFadeState:
_isSelected ? CrossFadeState.showSecond : CrossFadeState.showFirst,
);
}
}
class StarSelectionInterface {
void onStarSelected(_RatingStarWidgetState starWidgetState) {}
}
Flutterの方法は、必要なときにウィジェットを再構築することです。ウィジェットを作成することを恐れないでください。ウィジェットは、特にこの場合は単純なスターの場合、SDKにとって安価です。
別のウィジェット状態にアクセスするには、単にそれを再構築するよりも多くの作業が必要です。状態にアクセスするには、キーを使用するか、ウィジェット自体に特別なメソッドを追加する必要があります。
この場合、スターが何であれ再構築される場合、選択された状態は親が再構築の瞬間に提供できるため、プレーンなステートレスウィジェットを使用するほうがより簡単です。
状態は親ウィジェットに保存されるので、個々の星のそれぞれに壁として保存しない方がいいと思います。
次はその考えに従う非常に簡単な解決策です。しかし、はい、それでも星を再構築します。
import 'package:flutter/material.Dart';
import 'package:font_awesome_flutter/font_awesome_flutter.Dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(body: Center(child: StarRatingWidget())),
);
}
}
class StarRatingWidget extends StatefulWidget {
@override
_StarRatingWidgetState createState() {
return _StarRatingWidgetState();
}
}
class _StarRatingWidgetState extends State<StarRatingWidget> {
int _currentRating = 0;
List<Widget> buildStars() {
List<RatingStarWidget> starWidgets = [];
for (int i = 0; i < 5; i++) {
starWidgets.add(
RatingStarWidget(
clickCallback: () => setState(() {
_currentRating = i + 1;
}),
highlighted: _currentRating > i,
),
);
}
return starWidgets;
}
@override
Widget build(BuildContext buildContext) {
return Row(
children: buildStars(),
);
}
}
class RatingStarWidget extends StatelessWidget {
//Properties
final VoidCallback clickCallback;
final bool highlighted;
//Constructors
RatingStarWidget({this.clickCallback, this.highlighted});
@override
StatelessElement createElement() {
print("Element created");
return super.createElement();
}
//Methods
@override
Widget build(BuildContext buildContext) {
return GestureDetector(
onTap: () {
clickCallback();
},
child: AnimatedCrossFade(
firstChild: Icon(
FontAwesomeIcons.star,
size: 14,
),
secondChild: Icon(
FontAwesomeIcons.solidStar,
size: 14,
),
duration: Duration(milliseconds: 300),
crossFadeState:
highlighted ? CrossFadeState.showSecond : CrossFadeState.showFirst,
),
);
}
}
私はあなたに似た独自の例を書きました。私がここですることは:
配列は0から始まるため、初期スターレートは-1です;)そして、位置、現在のスターレート、およびコールバック関数でスターを作成します。このコールバック関数を使用して、ScreenOne
の値を更新します。
Star
ウィジェットには、デフォルト値falseのローカルブールselected
があり、スターの位置と現在のレートに基づいて、ビルド関数内に値を割り当てます。そして、コールバック関数を実行し、currentRate
を星の位置の値で更新するsetSelected()
関数があります。
ビデオの例 こちら を確認してください。
class ScreenOne extends StatefulWidget {
@override
_ScreenOneState createState() => _ScreenOneState();
}
class _ScreenOneState extends State<ScreenOne> {
int currentRate = -1; //since array starts from 0, set non-selected as -1
List<Star> starList = []; //empty list
@override
void initState() {
super.initState();
buildStars(); //build starts here on initial load
}
Widget buildStars() {
starList = [];
for (var i = 0; i < 5; i++) {
starList.add(Star(
position: i,
current: currentRate,
updateParent: refresh, //this is callback
));
}
}
refresh(int index) {
setState(() {
currentRate = index; //update the currentRate
});
buildStars(); //build stars again
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Test page 1"),
),
body: Container(
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: starList,
),
),
),
);
}
}
class Star extends StatefulWidget {
final Function(int index) updateParent; //callback
final int position; //position of star
final int current; //current selected star from parent
const Star({Key key, this.position, this.updateParent, this.current})
: super(key: key);
@override
_StarState createState() => _StarState();
}
class _StarState extends State<Star> {
bool selected = false;
void setSelected() {
widget.updateParent(widget.position);
}
@override
Widget build(BuildContext context) {
if (widget.current >= widget.position) {
selected = true;
} else {
selected = false;
}
return GestureDetector(
child: AnimatedCrossFade(
firstChild: Icon(Icons.star_border),
secondChild: Icon(Icons.star),
crossFadeState:
selected ? CrossFadeState.showSecond : CrossFadeState.showFirst,
duration: Duration(milliseconds: 300),
),
onTap: () {
setSelected();
},
);
}
}