Я получаю ошибку setState () или markNeedsBuild (), вызванную во время сборки

#flutter #setstate

#сбой #setstate

Вопрос:

Я пытаюсь показать эффект мерцания перед загрузкой изображения в мое приложение, но я получаю сообщение об ошибке setState() or markNeedsBuild() called during build.

 ...some code here...
       child: Card(
         elevation: 5,
         child: ListView.builder(
           shrinkWrap: true,
           scrollDirection: Axis.horizontal,
           physics: ScrollPhysics(),
           itemCount: widget.images.length,
           itemBuilder: (context, index) {
             var _image=widget.images[index];
             bool showImage=true;
             //todo check setState error
               NetworkImage(_image).resolve(ImageConfiguration()).addListener(
                 ImageStreamListener(
                       (info, call) {
                     if(mounted) {
                       setState(() {
                         showImage = true;
                       });
                     }
                     // do something
                   },
                 ),
               );

             return showImage?Container(
                 width: screenWidth(context)*0.4,
                 height:screenHeight(context)*0.2,
                 child: Image.network(
                   widget.images[index],
                   width: MediaQuery.of(context).size.width*0.5,
                   fit: BoxFit.cover,
                 ),
               )
                   :Container(
               width: screenWidth(context)*0.4,
               height:screenHeight(context)*0.2,
                 child: Shimmer.fromColors(
...some code here...

  

Я понял, что этот код является нарушителем после того, как прокомментировал его, но я действительно хотел бы сохранить его для эффекта мерцания.

                NetworkImage(_image).resolve(ImageConfiguration()).addListener(
                 ImageStreamListener(
                       (info, call) {
                     if(mounted) {
                       setState(() {
                         showImage = true;
                       });
                     }
                     // do something
                   },
                 ),
               );
  

Как я могу это обойти?

Ответ №1:

Это потому, что вы пытаетесь установить setState во время выполнения процесса сборки. Ваш прослушиватель запущен на этапе сборки. Попробуйте использовать addPostFrameCallback:

 ImageStreamListener(
  (info, call) {
    if(mounted) {
      WidgetsBinding.instance.addPostFrameCallback((_){
        setState(() {
          showImage = true;
        });
      });
    }
  },
  true 
),
  

Комментарии:

1. похоже, что ImageStreamListener принимает только один аргумент. При выполнении этого я получаю сообщение об ошибке.

2. Извините, я виноват. Я обновляю ответ.

3. Спасибо, теперь это то, что я искал.

Ответ №2:

Вы можете скопировать вставить запустить полный код ниже
Вы можете использовать package https://pub.dev/packages/cached_network_image
и помещаю Shimmer в placeholder

 CachedNetworkImage(
      imageUrl: "http://via.placeholder.com/350x150",
      placeholder: (context, url) => Shimmer.fromColors(
        baseColor: Colors.red,
        highlightColor: Colors.yellow,
        child: Text(
          'Shimmer',
          textAlign: TextAlign.center,
          style: TextStyle(
            fontSize: 40.0,
            fontWeight:
            FontWeight.bold,
          ),
        ),
      ),
      errorWidget: (context, url, error) => Icon(Icons.error),
    ),
  

рабочая демонстрация

введите описание изображения здесь

полный код

 import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:shimmer/shimmer.dart';

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

class MyApp extends StatelessWidget {  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(       
        primarySwatch: Colors.blue,       
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      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> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {     
      _counter  ;
    });
  }

  @override
  Widget build(BuildContext context) {    
    return Scaffold(
      appBar: AppBar(       
        title: Text(widget.title),
      ),
      body: Center(        
        child: Column(          
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            CachedNetworkImage(
              imageUrl: "http://via.placeholder.com/350x150",
              placeholder: (context, url) => Shimmer.fromColors(
                baseColor: Colors.red,
                highlightColor: Colors.yellow,
                child: Text(
                  'Shimmer',
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    fontSize: 40.0,
                    fontWeight:
                    FontWeight.bold,
                  ),
                ),
              ),
              errorWidget: (context, url, error) => Icon(Icons.error),
            ),
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}