Getting an image-related exception when using a ListWheelScrollView in Flutter

Viewed 201

I'm getting a peculiar bug when using a ListWheelScrollView to display image widgets on iOS. It is contained in one page of a PageView, and it works fine until I minimize the app. If the app is resumed after entering the background and then I switch to the page that contains the scrollview (either by switching away and switching back after resuming or by switching away before minimizing and then switching back after resuming), the visible images fail to display and the output reads as follows:

════════ Exception caught by image resource service ════════════════════════════ The method 'toDouble' was called on null. Receiver: null Tried calling: toDouble() ════════════════════════════════════════════════════════════════════════════════

Below is a simple example that demonstrates the problem:

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ListWheel Issue',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'ListWheelScrollview Bug'),
    );
  }
}

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  int _currentIndex = 0;
  PageController _pageController;
  List<Widget> get _tabs => [
        ScrollScreen(),
        Container(),
        Container(),
      ];

  @override
  void initState() {
    _pageController = PageController();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
          child: PageView(
        controller: _pageController,
        children: _tabs,
      )),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(
              icon: Icon(Icons.refresh), title: Text('tab1')),
          BottomNavigationBarItem(icon: Icon(Icons.movie), title: Text('tab2')),
          BottomNavigationBarItem(
              icon: Icon(Icons.search), title: Text('tab3')),
        ],
        currentIndex: _currentIndex,
        onTap: (i) {
          setState(() {
            _currentIndex = i;
            _pageController.animateToPage(i,
                curve: Curves.easeOut, duration: Duration(milliseconds: 200));
          });
        },
      ),
    );
  }
}

class ScrollScreen extends StatefulWidget {
  @override
  _ScrollScreenState createState() => _ScrollScreenState();
}

class _ScrollScreenState extends State<ScrollScreen> {
  var _exImage = AssetImage('assets/images/no_image.png'); 

  @override
  Widget build(BuildContext context) {
    List<AssetImage> _images = [
      _exImage,
      _exImage,
      _exImage,
    ];
    return Center(
      child: ListWheelScrollView.useDelegate(
          itemExtent: 300,
          childDelegate: ListWheelChildLoopingListDelegate(
              children: _images
                  .map((e) => Center(
                          child: Image(
                        image: e,
                      )))
                  .toList())),
    );
  }
}

Any help with this would be greatly appreciated.

2 Answers

I found out a solution.

 @override
 void didChangeDependencies() {
 super.didChangeDependencies();
 precacheImage(AssetImage('assets/icons/settings_icon.png'), context);
}

after precaching the images used in the ListWheelScrollView, the error no longer appears

Idk if flutter team will fix it in the future or not.

I figured out a bit more about the issue. It turns out null values were being passed as "minScrollExtent" and "maxScrollExtent" into the _getItemFromOffset function in the list_wheel_scroll_view.dart file, which were, in turn being passed into the _clipOffsetToScrollableRange function.

As a workaround, I ended up changing the return value of the _clipOffsetScrollableRange to the following:

return (_clipOffsetToScrollableRange(
          offset,
          minScrollExtent ?? -double.infinity,
          maxScrollExtent ?? double.infinity) /
      itemExtent)
  .round();

This likely does not address the underlying issue, but has resulted in my app working properly.

Related