Flutter 如何强制转换为未知的泛型运行时类型(C ChangeType等效)

Flutter 如何强制转换为未知的泛型运行时类型(C ChangeType等效),flutter,dart,Flutter,Dart,我对Flutter/Dart是全新的,我正在尝试构建一个可重用的带有占位符加载的无限滚动条。课程安排如下: import 'dart:async'; import 'package:flutter/material.dart'; class PagedScroller<T> extends StatefulWidget { final int limit; final Future<List<T>> Function(int, int) getDa

我对Flutter/Dart是全新的,我正在尝试构建一个可重用的带有占位符加载的无限滚动条。课程安排如下:

import 'dart:async';

import 'package:flutter/material.dart';

class PagedScroller<T> extends StatefulWidget {
  final int limit;
  final Future<List<T>> Function(int, int) getDataFunction;
  final Widget Function(T) renderFunction;
  final Widget Function() renderPlaceholderFunction;

  PagedScroller(
      {@required this.limit,
      @required this.getDataFunction,
      @required this.renderFunction,
      @required this.renderPlaceholderFunction});

  @override
  _PagedScrollerState<T> createState() => _PagedScrollerState<T>();
}

class _PagedScrollerState<T> extends State<PagedScroller> {
  int _offset = 0;
  int _lastDataLength = 1; // Init to one so the first call can happen
  List<dynamic> _items = [];
  Future<List<dynamic>> _future;
  bool _isInitializing = false;
  bool _isInitialized = false;
  bool _isLoading = false;
  ScrollController _controller =
      ScrollController(initialScrollOffset: 0.0, keepScrollOffset: true);

  _PagedScrollerState();

  void _init() {
    _isInitializing = true;

    _reset();

    _controller.addListener(() {
      bool loadMore = false;
      if (_controller.position.maxScrollExtent == double.infinity) {
        loadMore = _controller.offset == _controller.position.maxScrollExtent;
      } else {
        loadMore =
            _controller.offset >= _controller.position.maxScrollExtent * 0.85;
      }

      // Only load more if it's not currently loading and we're not on the last page
      // _lastDataLength should be 0 if there are no more pages
      if (loadMore && !_isLoading && _lastDataLength > 0) {
        _offset += widget.limit;
        _load();
      }
    });

    _load();
    _isInitializing = false;
    _isInitialized = true;
  }

  void _reset() {
    // Clear things array and reset inital get-things link (without paging)
    setState(() {
      _future = _clearThings();
    });

    // Reload things
    // Reset to initial GET link
    _offset = 0;
  }

  void _load() {
    setState(() {
      _future = _loadPlaceholders();
      _future = _loadData();
    });
  }

  Future<List<dynamic>> _clearThings() async {
    _items.clear();
    return Future.value(_items);
  }

  Future<List<dynamic>> _loadPlaceholders() async {
    // Add 20 empty placeholders to represent stuff that's currently loading
    for (var i = 0; i < widget.limit; i++) {
      _items.add(_Placeholder());
    }

    return Future.value(_items);
  }

  List<dynamic> _getInitialPlaceholders() {
    var placeholders = List<dynamic>();
    for (var i = 0; i < widget.limit; i++) {
      placeholders.add(_Placeholder());
    }
    return placeholders;
  }

  Future<List<dynamic>> _loadData() async {
    _setLoading(true);

    var data = await widget.getDataFunction(widget.limit, _offset);

    // When loading data is done, remove any placeholders
    _items.removeWhere((item) => item is _Placeholder);

    // If 0 items were returned, it's probably the last page
    _lastDataLength = data.length;

    for (var item in data) {
      _items.add(item);
    }

    _setLoading(false);

    return Future.value(_items);
  }

  void _setLoading(bool isLoading) {
    if (!mounted) {
      return;
    }

    setState(() {
      _isLoading = isLoading;
    });
  }

  Future<void> _refreshThings() async {
    _reset();
    _load();
    return Future;
  }

  @override
  Widget build(BuildContext context) {
    if (!_isInitializing && !_isInitialized) {
      _init();
    }

    return FutureBuilder(
      future: _future,
      initialData: _getInitialPlaceholders(),
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        if (snapshot.hasData) {
          List<dynamic> loadedItems = snapshot.data;
          return RefreshIndicator(
            onRefresh: _refreshThings,
            child: ListView.builder(
              itemCount: loadedItems.length,
              controller: _controller,
              physics: const AlwaysScrollableScrollPhysics(),
              itemBuilder: (BuildContext context, int index) {
                var item = loadedItems[index];
                if (item is _Placeholder) {
                  return widget.renderPlaceholderFunction();
                } else if (item is T) {
                  // THIS IS THE LINE THAT FAILS 
                  return widget.renderFunction(item);
                }

                return Text('Unknown item type');
              },
            ),
          );
        }
        return Container();
      },
    );
  }
}

class _Placeholder {}
在以下情况下失败:

type '(MyModel) => Widget' is not a subtype of type '(dynamic) => Widget'
Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=BUG.md
我明白为什么会这样。编译器无法知道我的PagedScroller中的类型t与_PagedScrollerState中的类型t相同。因此,Dart试图提供帮助,并将我的回调函数类型Widget FunctionT转换为Widget Functiondynamic

然后我想也许我可以用下面的话来假装,因为我知道PagedScroller和_PagedScrollerState中的T总是一样的:

var renderFunction = widget.renderFunction as Widget Function(T);
return renderFunction(item);
有趣的是,这给了我一个警告:

Unnecessary cast.
Try removing the cast.
但它甚至不会运行该行,因为以下原因而崩溃:

type '(MyModel) => Widget' is not a subtype of type '(dynamic) => Widget'
Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=BUG.md
将所有内容更改为dynamic很有吸引力,但是如果不需要的话,我真的不想失去泛型的可读性

尽管进行了广泛的搜索,但我找不到与C的Convert.ChangeType等价的版本,您可以在运行时提供类型,这样我就可以执行我想要的转换并完成它

这似乎是一件非常简单的事情,但我被卡住了

您可以使用以下简单的main.dart复制/粘贴来使用滚动条:

import 'package:flutter/material.dart';
import 'package:minimal_repros/paged_scroller.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    Future<List<MyModel>> getDataFunction(int limit, int offset) async {
      var myModels = List<MyModel>();

      // Simulate API call
      await Future.delayed(Duration(milliseconds: 1000));

      for (int i = 0; i < limit; i++) {
        var myModel = MyModel();
        myModel.count = i + offset;
        myModel.firstName = 'Bob';
        myModels.add(myModel);
      }

      return myModels;
    }

    Widget renderFunction(MyModel myModel) {
      return Text(myModel.firstName);
    }

    Widget renderPlaceholderFunction() {
      return Text('Loading');
    }

    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: PagedScroller(
            getDataFunction: getDataFunction,
            renderFunction: renderFunction,
            renderPlaceholderFunction: renderPlaceholderFunction,
            limit: 20));
  }
}

class MyModel {
  int count;
  String firstName;
}


在State类的声明中,您忘记指定小部件的泛型参数

而不是:

类_PagedScrollerState扩展状态{ 做:

类_PagedScrollerState扩展状态{
在State类的声明中,您忘记指定小部件的泛型参数

而不是:

类_PagedScrollerState扩展状态{ 做:

类_PagedScrollerState扩展状态{
这么多的源代码。所有这些源代码真的有必要理解这个问题的含义吗?这么多的源代码。所有这些源代码真的有必要理解这个问题的含义吗?不要!我们是人类:但是考虑禁用分析动态中隐含的动态。YAMLI没有意识到这一点。希望它是一个默认的。我们是人:但是考虑在分析分析中禁用隐式动态。YAMLI没有意识到这一点。希望它是一个默认的。