Flutter以编程方式触发FutureBuilder

时间:2018-08-02 23:22:22

标签: dart flutter

假设我有这样的东西:

return FutureBuilder(
  future: _loadingDeals,
  builder: (BuildContext context, AsyncSnapshot snapshot) {
    return RefreshIndicator(
      onRefresh: _handleRefresh,
        ...
    )
  }
 )

_handleRefresh方法中,我要以编程方式触发FutureBuilder的重新运行。

有这样的东西吗?

用例:

当用户拉下refreshIndicator时,_handleRefresh只会使FutureBuilder重新运行。

修改

完整的代码段首尾相连,没有刷新的部分。我已改用StreamBuilderrefreshIndicator部分将如何适合所有部分?

class DealList extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new _DealList();
}

class _DealList extends State<DealList> with AutomaticKeepAliveClientMixin {
  // prevents refreshing of tab when switch to
  // Why? https://stackoverflow.com/q/51224420/1757321
  bool get wantKeepAlive => true; 

  final RestDatasource api = new RestDatasource();
  String token;
  StreamController _dealsController;

  @override
  void initState() {
    super.initState();
    _dealsController = new StreamController();
    _loadingDeals();
  }

  _loadingDeals() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();

    this.token = prefs.getString('token');

    final res =
        this.api.checkInterests(this.token).then((interestResponse) async {
      _dealsController.add(interestResponse);
      return interestResponse;
    });
    return res;
  }

  _handleRefresh(data) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();

    final token = prefs.getString('token');
    await this.api.checkInterests(token).then((interestResponse) {
      _dealsController.add(interestResponse);
    });
    return null;
  }

  @override
  Widget build(BuildContext context) {
    super.build(context); // <-- this is with the wantKeepAlive thing
    return StreamBuilder(
      stream: _dealsController.stream,
      builder: (BuildContext context, AsyncSnapshot snapshot) {

        if (snapshot.hasError) {
          ...
        }

        if (snapshot.connectionState != ConnectionState.done) {
          return Center(
            child: CircularProgressIndicator(),
          );
        }

        if (!snapshot.hasData &&
            snapshot.connectionState == ConnectionState.done) {
          return Text('No deals');
        }

        if (snapshot.hasData) {
          return ListView.builder(
                physics: const AlwaysScrollableScrollPhysics(),
                itemCount: snapshot.data['deals'].length,
                itemBuilder: (context, index) {
                  final Map deal = snapshot.data['deals'][index];
                  return ListTile(
                      onTap: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (context) => DealsDetailPage(
                                  dealDetail: deal,
                                ),
                          ),
                        );
                      },
                      title: Text(deal['name']),
                      subtitle: Text(deal['expires']),
                    );
                },
              ),
        }
      },
    );
  }
}

3 个答案:

答案 0 :(得分:4)

为什么不使用StreamBuilder和Stream代替FutureBuilder?

类似的东西...

/^[a-z$_][$\w]*(\.[a-z$_][$\w]*)*$/i

我使用StreamBuilder check it out

用Flutter主要示例创建了一个Gist。

答案 1 :(得分:0)

使用StreamBuilder是一种解决方案,但是,以编程方式触发FutureBuilder,只需调用setState,它将重新构建Widget。

class MedicalConditionSelectionReactor: Reactor {
  
  // represent user actions
  enum Action {
    case loadConditions
    case addNewCondition
    case removeCondition
    case toggleCondition(index: Int)
    case save
  }

  // represent state changes
  enum Mutation {
    case loadedMedicalConditions([MedicalConditionSelectionSectionItem])
    case reloadList
  }

  // represents the current view state
  struct State {
    var sections: [MedicalConditionSelectionSection] = [MedicalConditionSelectionSection(items: [])]
  }

  let initialState: State = State()
  
  func mutate(action: Action) -> Observable<Mutation> {
    switch action {
    case .loadConditions:
      let medicalConditions = OptionsModelDBProvider<RealmMedicalConditionModel>().fetch()
      let conditions = medicalConditions.toArray().map { item -> MedicalConditionSelectionSectionItem in
        // let isSelected = arrayOfMedicalConditionID.contains("\(item.ID)")
        let medicalCell = MedicineCellReactor(id: "\(item.ID)", title: item.name, isSelected: false)
        return MedicalConditionSelectionSectionItem.medicalCondition(medicalCell)
      }
      return Observable.of(Mutation.loadedMedicalConditions(conditions))
      
    case let .toggleCondition(indexOfCell):
      let state = currentState.sections[0].items[indexOfCell]
      switch state {
      case let .medicalCondition(reactor):
        // TODO:- How to toggle the isSelected bool property of MedicineCellReactor.State. Since this is immutable.
        // reactor.currentState.isSelected = false
        break
      default: break
      }
      return Observable.of(Mutation.reloadList)
      
      
    default:
      return Observable.of(Mutation.reloadList)
    }
  }
  
  func reduce(state: State, mutation: Mutation) -> State {
    var state = state
    switch mutation {
    case let .loadedMedicalConditions(conditions):
      state.sections[0].items = conditions
    default:
      break
    }
    return state
  }

}

答案 2 :(得分:0)

我更喜欢 FutureBuilder 而不是 StreamBuilder,因为我在我的项目中使用 Firestore 并且您按读取收费,所以我的解决方案是这样的

    _future??= getMyFuture();

    shouldReload(){
       setState(()=>_future = null)
    }
    
    FutureBuilder(
       future: _future, 
       builder: (context, snapshot){
    return Container();
    },
   )
 

以及任何需要您获取新数据的用户活动,只需调用 shouldReload()