簡體   English   中英

Flutter BLoC:當父小部件更新時保持子狀態

[英]Flutter BLoC: Maintaining Child State when Parent Widget Updates

我現在有一個應用程序可以創建一個計時器列表。 它使用兩個 BLoC,一個用於單個計時器,另一個用於整個列表。 您可以看到以下列表的示例: 示例列表

在圖像中,您可以看到索引 0 和 2 尚未啟動,而索引 1 已啟動。 我的問題是,每當我從列表中添加或刪除項目時,所有計時器都會重置。

當父小部件被重繪時,有沒有辦法讓它們的狀態保持不變?

這是列表代碼:

這里的 Item Tile 包含計時器的 BLoC:

  class HomePage extends StatefulWidget {
  static const TextStyle timerTextStyle = TextStyle(
    fontSize: 30,
    fontWeight: FontWeight.bold,
  );

  final Stream shouldTriggerChange;
  HomePage({@required this.shouldTriggerChange});
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  StreamSubscription streamSubscription;

  @override
  initState() {
    super.initState();
    streamSubscription = widget.shouldTriggerChange.listen((data) {
      createTimer(context, data);
    });
  }

  void createTimer(BuildContext context, timer_type timer) {
    BlocProvider.of<ListBloc>(context).add(Add(
        id: DateTime.now().toString(),
        duration: timer == timer_type.timer ? 100 : 0,
        timer: timer,
        timerTextStyle: HomePage.timerTextStyle));
  }

  @override
  didUpdateWidget(HomePage old) {
    super.didUpdateWidget(old);
    // in case the stream instance changed, subscribe to the new one
    if (widget.shouldTriggerChange != old.shouldTriggerChange) {
      streamSubscription.cancel();
      streamSubscription = widget.shouldTriggerChange
          .listen((data) => createTimer(context, data));
    }
  }

  @override
  dispose() {
    super.dispose();
    streamSubscription.cancel();
  }

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<ListBloc, ListState>(
      builder: (context, state) {
        if (state is Failure) {
          return Center(
            child: Text('Oops something went wrong!'),
          );
        }
        if (state is Loaded) {
          return Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              state.timers.isEmpty
                  ? Center(
                      child: Text('no content'),
                    )
                  : Expanded(
                      child: ListView.builder(
                        itemBuilder: (BuildContext context, int index) {
                          return ItemTile(
                            index: index,
                            timer: state.timers[index],
                            onDeletePressed: (id) {
                              BlocProvider.of<ListBloc>(context)
                                  .add(Delete(id: id));
                            },
                          );
                        },
                        itemCount: state.timers.length,
                      ),
                    ),
            ],
          );
        }
        return Center(
          child: CircularProgressIndicator(),
        );
      },
    );
  }
}

最初我認為這是一個關鍵問題,因為列表沒有正確刪除,但我相信我已經按照示例中顯示的方式實現了鍵,但仍然存在問題。

class ItemTile extends StatelessWidget {
  final key = UniqueKey();
  final Function(String) onDeletePressed;
  final int index;
  final Timer timer;
  ItemTile({
    Key key,
    @required this.index,
    @required this.timer,
    @required this.onDeletePressed,
  }) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Row(
        children: [
          Text('${index.toString()}'),
          BlocProvider(
            create: (context) => TimerBloc(ticker: Ticker(), timer: timer),
            child: Container(
              height: (MediaQuery.of(context).size.height - 100) / 5,
              child: Row(
                children: [
                  Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      Padding(
                        padding: EdgeInsets.symmetric(vertical: 10.0),
                        child: Center(
                          child: BlocBuilder<TimerBloc, TimerState>(
                            builder: (context, state) {
                              final String minutesStr =
                                  ((state.duration / 60) % 60)
                                      .floor()
                                      .toString()
                                      .padLeft(2, '0');
                              final String secondsStr = (state.duration % 60)
                                  .floor()
                                  .toString()
                                  .padLeft(2, '0');
                              return Text(
                                '$minutesStr:$secondsStr',
                              );
                            },
                          ),
                        ),
                      ),
                      BlocBuilder<TimerBloc, TimerState>(
                        buildWhen: (previousState, currentState) =>
                            currentState.runtimeType !=
                            previousState.runtimeType,
                        builder: (context, state) => TimerActions(),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),
          timer.isDeleting
              ? CircularProgressIndicator()
              : IconButton(
                  icon: Icon(Icons.delete, color: Colors.red),
                  onPressed: () {
                    onDeletePressed(timer.id);
                  },
                ),
        ],
      ),
    );
  }
}

這是在列表 BLoC 中傳遞的計時器模型:

class Timer extends Equatable {
  final timer_type timer;
  final int duration;
  final String id;
  final bool isDeleting;
  const Timer({
    @required this.id,
    @required this.timer,
    @required this.duration,
    this.isDeleting = false,
  });
  Timer copyWith({timer_type timer, int duration, String id, bool isDeleting}) {
    return Timer(
      id: id ?? this.id,
      duration: duration ?? this.duration,
      timer: timer ?? this.timer,
      isDeleting: isDeleting ?? this.isDeleting,
    );
  }

  @override
  List<Object> get props => [id, duration, timer, isDeleting];
  @override
  String toString() =>
      'Item { id: $id, duration: $duration, timer type: $timer, isDeleting: $isDeleting }';
}

任何幫助將不勝感激。

謝謝!

每次重建列表時,您都會創建一個新的UniqueKey ,這會強制刪除狀態

因此,要修復它,您必須將鍵與計時器相關聯,例如將計時器包裝在一個包含計時器和鍵的類中,如下所示:

class KeyedTimer {
  final key = UniqueKey();
  Timer timer;
}

所以現在您將擁有一個 KeyedTimer 對象列表而不是計時器列表,您可以在列表的 itembuilder 中使用該鍵

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM