首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在滚动视图到达中间时从api中添加新项到颤振网格视图生成器中

如何在滚动视图到达中间时从api中添加新项到颤振网格视图生成器中
EN

Stack Overflow用户
提问于 2020-10-17 17:20:35
回答 3查看 2.3K关注 0票数 3

当滚动视图到达尾端时,我试图更新模型的列表,虽然我能够这样做,但我无法顺利地完成它。我希望每当用户到达滚动卷的中间时,将新图像从API加载到模型列表中,这样滚动就会像在当前场景中一样平稳,当用户到达末尾时,会有一两秒钟的暂停,然后添加新元素,这样用户就不会知道是否添加了新的元素。另外,我目前的方法是在主线程上增加太多的负载,并使应用程序崩溃。

下面的代码如下:

home.dart

代码语言:javascript
运行
复制
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:wallpaper_app/data/data.dart';
import 'package:wallpaper_app/model/categories.dart';
import 'package:wallpaper_app/model/wallpaper_model.dart';
import 'package:wallpaper_app/network/network.dart';
import 'package:wallpaper_app/widgets/branding.dart';
import 'package:wallpaper_app/widgets/list_tile.dart';
import 'package:wallpaper_app/widgets/wallpaper_tile.dart';
var pageIndex=1;
Data data = Data();
List<CategoriesModel> categories;
List<WallpaperModel> wallpapers =[];

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _controller = ScrollController();
    _controller.addListener(() {
      if (_controller.position.atEdge) {
        if (_controller.position.pixels == 0)
          print('top');
      // you are at top position
      else{
          print('end');
          incrementIndex();
          getWallpapers(pageIndex);
        }

      // you are at bottom position
    }
    });
    categories = data.getCategories();
    getWallpapers(pageIndex);
  }
  ScrollController _controller;

  void incrementIndex(){
    setState(() {
      pageIndex++;
    });
  }

  void getWallpapers(var index) async {
    try {
      Network network = Network();
      Map<String, dynamic> jsonData = await network.getCuratedWallpapers(
          index, 80);
      jsonData["photos"].forEach((element) {
        WallpaperModel wallpaperModel = WallpaperModel.fromMap(element);
        setState(() {
          wallpapers.add(wallpaperModel);
        });
//      for (var wallpaper in wallpapers){
//        print(wallpaper.src.small);
//      }
      });
    }
    catch(e){
      print(e);
    }
  }

  @override
  Widget build(BuildContext context) {
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
      systemNavigationBarColor: Color(0xff999999), // navigation bar color
    ));
    return Scaffold(
      appBar: AppBar(
        elevation: 0,
        backgroundColor: Colors.white,
        title: Brand(),
      ),
      body: SingleChildScrollView(
        controller: _controller,
        child: Container(
          child: Column(
            children: [
              Container(
                height: 100,
                child: ListView.builder(
//                  controller: _controller,
                    padding: EdgeInsets.all(10),
                    itemCount: categories.length,
//                  shrinkWrap: true,
                    scrollDirection: Axis.horizontal,
                    itemBuilder: (context, index) {
                      return CategoriesTile(
                        imageUrl: categories[index].imageUrl,
                        title: categories[index].categoriesName,
                      );
                    }),
              ),
              SizedBox(
                height: 20,
              ),
              Container(
                padding: EdgeInsets.symmetric(horizontal: 10),
                child: GridView.builder(
                  physics: ScrollPhysics(),
                  shrinkWrap: true,
                    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                        crossAxisCount: 2,
                        crossAxisSpacing: 6.0,
                        mainAxisSpacing: 6.0,
                        childAspectRatio: 0.6),
                    itemBuilder: (context,index){
//                      if(index==50){
////                        incrementIndex();
//                        getWallpapers(pageIndex+1);
//                      }
                      return WallpaperTile(small: wallpapers[index].src.portrait,);
                    },
                  itemCount: wallpapers.length,
                    ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

wallpaper_model.dart

代码语言:javascript
运行
复制
class WallpaperModel {
  String photographer;
  String photographerUrl;
  int photographerId;
  SrcModel src;
  WallpaperModel(
      {this.photographer, this.photographerUrl, this.photographerId, this.src});
  factory WallpaperModel.fromMap(Map<String,dynamic> jsonData){
    return WallpaperModel(
      photographer: jsonData["photographer"],
      photographerId: jsonData["photographer_id"],
      photographerUrl: jsonData["photographer_url"],
      src: SrcModel.fromMap(jsonData["src"])
    );
  }
}

class SrcModel {
  String original;
  String small;
  String portrait;
  SrcModel({this.original, this.small, this.portrait});
  factory SrcModel.fromMap(Map<String, dynamic> jsonData) {
    return SrcModel(
        original: jsonData["original"],
        small: jsonData["small"],
        portrait: jsonData["portrait"]);
  }
}

wallpaper_tile.dart

代码语言:javascript
运行
复制
import 'package:flutter/material.dart';

class WallpaperTile extends StatelessWidget {
  final String small;
  WallpaperTile({this.small});
  @override
  Widget build(BuildContext context) {
    return Container(
      child: ClipRRect(
        borderRadius: BorderRadius.circular(20),
          child:
          Image.network(
        small,
        fit: BoxFit.cover,
      )),
    );
  }
}

network.dart

代码语言:javascript
运行
复制
import 'dart:convert';

import 'package:http/http.dart' as http;
const apiKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
class Network{
 Future<dynamic> getCuratedWallpapers(var pageNo, var maxWallpapers) async{

   var response = await http.get("https://api.pexels.com/v1/curated/?page=$pageNo&per_page=$maxWallpapers",
       headers: {
     "Authorization":apiKey
   });
   return jsonDecode(response.body);
 }
}

顶部列表视图仅用于类别,可以忽略。网格view.builder具有核心功能

EN

回答 3

Stack Overflow用户

发布于 2020-10-18 03:53:45

虽然我无法解决主要问题(每次滚动到屏幕中部时加载,这样用户就不必等待并查看进度条),但是我能够解决所有其他问题。

  1. 太多的图片(>3000)导致应用程序在没有堆栈跟踪的情况下崩溃。解决方案->我没有在singleChildScrollView中使用网格视图,而是将它添加到一个扩展小部件中,解决了这种崩溃和缓慢的滚动行为。
  2. 经过一定数量的页面之后,我开始清除ImageCache,因为从我正在搜索的内容中,我发现在压缩图像之后,它们仍然可以留在内存中(我仍然不知道这是一个内部颤振过程)。
  3. 我还使用了与ChangeNotifier一起的提供者,而不是setState,以避免小部件的额外重建。结果:尽管UI不是超级平滑的,滚动正在无缝地进行,图像在没有视图时会自动被清除。还有第二个进度条,然后将API调用的下一个页面加载到列表中。这是我在某种程度上延迟加载的实现。当另一个调用使用布尔值运行时,我不会调用异步调用。
  4. 我还在每个图像中添加了缓存高度和缓存宽度,以便只在网格视图中加载压缩图像大小,而不是在其中加载完整大小的图像。
  5. 此外,每次滚动到页面底部时,我都使用滚动控制器将更多的元素加载到列表中。以下是主要的变化。

main.dart

代码语言:javascript
运行
复制
import 'package:flutter/material.dart';
import 'package:wallpaper_app/data/provider_data.dart';
import 'package:wallpaper_app/screens/home.dart';
import 'package:provider/provider.dart';
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context)=>ProviderData(),
      child: MaterialApp(
        theme: ThemeData.dark().copyWith(
          scaffoldBackgroundColor: Colors.black,
        ),
        title: 'Wallpaper App',
        home: Home()
      ),
    );
  }
}

home.dart

代码语言:javascript
运行
复制
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:wallpaper_app/data/data.dart';
import 'package:wallpaper_app/data/provider_data.dart';
import 'package:wallpaper_app/model/categories.dart';
import 'package:wallpaper_app/model/wallpaper_model.dart';
import 'package:wallpaper_app/network/network.dart';
import 'package:wallpaper_app/widgets/branding.dart';
import 'package:wallpaper_app/widgets/list_tile.dart';
import 'package:wallpaper_app/widgets/wallpaper_tile.dart';
import 'package:provider/provider.dart';
import 'package:flutter/services.dart';
import 'package:flutter_pagewise/flutter_pagewise.dart';
Data data = Data();
List<CategoriesModel> categories;
class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    categories = data.getCategories();
    WidgetsBinding.instance.addPostFrameCallback((_) async {
      _controller = ScrollController();
      _controller.addListener(() {
        if (_controller.position.atEdge) {
          if (_controller.position.pixels == 0)
            print('top');
          // you are at top position
          else{
            print('end');
//          incrementIndex();
            if(Provider.of<ProviderData>(context,listen: false).pageIndex>5){
              print('image cache: ${imageCache.currentSize}');
              imageCache.clear();
            }
            Provider.of<ProviderData>(context,listen: false).incrementPage();
            getWallpapers(Provider.of<ProviderData>(context,listen: false).pageIndex);
          }

          // you are at bottom position
        }
      });
      getWallpapers(Provider.of<ProviderData>(context,listen: false).pageIndex);
    });

  }
  ScrollController _controller;

  void getWallpapers(var index) async {
    if(!Provider.of<ProviderData>(context,listen: false).isPerformingRequest){
      Provider.of<ProviderData>(context,listen: false).togglePerformingRequest();

      try {
        Network network = Network();
        Map<String, dynamic> jsonData = await network.getCuratedWallpapers(
            index, 80);
        jsonData["photos"].forEach((element) {
          WallpaperModel wallpaperModel = WallpaperModel.fromMap(element);
          setState(() {
            Provider.of<ProviderData>(context,listen: false).addWallpapers(wallpaperModel);
          });
        });
      }
      catch(e){
        print(e);
      }
      Provider.of<ProviderData>(context,listen: false).togglePerformingRequest();
    }


  }

  @override
  Widget build(BuildContext context) {
  SystemChrome.setEnabledSystemUIOverlays([]);
    return Scaffold(
//      appBar: AppBar(
//        backgroundColor: Colors.black,
//        elevation: 0,
////        backgroundColor: Colors.white,
//        title: Brand(),
//      ),
      body:
//      SingleChildScrollView(
//        controller: _controller,
//        child:
        Container(
          child: Column(
            children: [
//              Container(
//                height: 100,
//                child: ListView.builder(
////                  controller: _controller,
//                    padding: EdgeInsets.all(10),
//                    itemCount: categories.length,
//                  shrinkWrap: true,
//                    scrollDirection: Axis.horizontal,
//                    itemBuilder: (context, index) {
//                      return CategoriesTile(
//                        imageUrl: categories[index].imageUrl,
//                        title: categories[index].categoriesName,
//                      );
//                    }),
//              ),
//              SizedBox(
//                height: 20,
//              ),
              Expanded(
//                padding: EdgeInsets.symmetric(horizontal: 10),
                child: GridView.builder(
                  controller: _controller,
                  physics: ScrollPhysics(),
                  shrinkWrap: true,
                  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                      crossAxisCount: 2,
                      crossAxisSpacing: 6.0,
                      mainAxisSpacing: 6.0,
                      childAspectRatio: 0.6),
                      addAutomaticKeepAlives: false,
                  itemBuilder: (context,index){
                    if(index==(Provider.of<ProviderData>(context,listen: false).wallpapers!=null?Provider.of<ProviderData>(context,listen: false).wallpapers.length+0:80)){
                      return _buildProgressIndicator();
                    }
                    else
                    return WallpaperTile(small: Provider.of<ProviderData>(context,listen: false).wallpapers[index].src.medium,);
                  },
                  itemCount: Provider.of<ProviderData>(context,listen: false).wallpapers!=null?Provider.of<ProviderData>(context,listen: false).wallpapers.length+1:81,
                ),
              )
            ],
          ),
        ),
//      ),
    );
  }
}

Widget _buildProgressIndicator() {
  return new Padding(
    padding: const EdgeInsets.all(8.0),
    child: new Center(
      child: new Opacity(
      opacity: 1,
        child: new CircularProgressIndicator(),
      ),
    ),
  );
}

wallpaper_model.dart

代码语言:javascript
运行
复制
class WallpaperModel {
  String photographer;
  String photographerUrl;
  int photographerId;
  SrcModel src;
  WallpaperModel(
      {this.photographer, this.photographerUrl, this.photographerId, this.src});
  factory WallpaperModel.fromMap(Map<String,dynamic> jsonData){
    return WallpaperModel(
      photographer: jsonData["photographer"],
      photographerId: jsonData["photographer_id"],
      photographerUrl: jsonData["photographer_url"],
      src: SrcModel.fromMap(jsonData["src"])
    );
  }
}

class SrcModel {
  String original;
  String small;
  String portrait;
  String medium;
  SrcModel({this.original, this.small, this.portrait,this.medium});
  factory SrcModel.fromMap(Map<String, dynamic> jsonData) {
    return SrcModel(
        original: jsonData["original"],
        small: jsonData["small"],
        medium: jsonData["medium"],
        portrait: jsonData["portrait"]);
  }
}

network.dart

代码语言:javascript
运行
复制
import 'dart:convert';

import 'package:http/http.dart' as http;
const apiKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
class Network{
 Future<dynamic> getCuratedWallpapers(var pageNo, var maxWallpapers) async{

   var response = await http.get("https://api.pexels.com/v1/curated/?page=$pageNo&per_page=$maxWallpapers",
       headers: {
     "Authorization":apiKey,
   });
   print(response.statusCode);
//   print(response.body);
   return jsonDecode(response.body);
 }
}

wallpaper_tile.dart

代码语言:javascript
运行
复制
import 'package:flutter/material.dart';

class WallpaperTile extends StatelessWidget {
  final String small;
  WallpaperTile({this.small});
  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.all(5),
      decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(25),
          color: Colors.grey
      ),
      child: ClipRRect(
        borderRadius: BorderRadius.circular(25),
          child:
          Image.network(
        small,
        fit: BoxFit.cover,
        cacheHeight: 400,
        cacheWidth: 225,
      )
      ),
    );
  }
}

provider_data.dart

代码语言:javascript
运行
复制
import 'package:flutter/foundation.dart';
import 'package:wallpaper_app/model/wallpaper_model.dart';

class ProviderData extends ChangeNotifier{
  List<WallpaperModel> wallpapers =[];
  bool isPerformingRequest = false;
  var pageIndex=1;

  void togglePerformingRequest(){
    isPerformingRequest = !isPerformingRequest;
    notifyListeners();
  }
  void incrementPage(){
    pageIndex++;
    print(pageIndex);
    notifyListeners();
  }

  void addWallpapers(WallpaperModel wallpaperModel){
    wallpapers.add(wallpaperModel);
    notifyListeners();
  }
}
票数 5
EN

Stack Overflow用户

发布于 2020-10-17 18:00:51

您需要将NotificationListenerScrollNotification结合使用。用您的需求来修改它。

代码语言:javascript
运行
复制
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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

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

class _MyHomePageState extends State<MyHomePage> {

  int present = 0;
  int perPage = 15;

  final originalItems = List<String>.generate(10000, (i) => "Item $i");
  var items = List<String>();


  @override
  void initState() {
    super.initState();
    setState(() {
      items.addAll(originalItems.getRange(present, present + perPage));
      present = present + perPage;
    });
  }

  void loadMore() {
    setState(() {
      if((present + perPage )> originalItems.length) {
        items.addAll(
            originalItems.getRange(present, originalItems.length));
      } else {
        items.addAll(
            originalItems.getRange(present, present + perPage));
      }
      present = present + perPage;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: NotificationListener<ScrollNotification>(
        onNotification: (ScrollNotification scrollInfo) {
          if (scrollInfo.metrics.pixels ==
              scrollInfo.metrics.maxScrollExtent / 2) {
            loadMore();
          }
        },
        child: ListView.builder(
          itemCount: (present <= originalItems.length) ? items.length + 1 : items.length,
          itemBuilder: (context, index) {
            return (index == items.length ) ?
            Container(
              color: Colors.greenAccent,
              child: FlatButton(
                child: Text("Load More"),
                onPressed: () {
                  loadMore();
                },
              ),
            )
                :
            ListTile(
              title: Text('${items[index]}'),
            );
          },
        ),
      ),
    );
  }
}

更多

票数 1
EN

Stack Overflow用户

发布于 2022-09-19 12:24:47

在您的GridView.builder中尝试SliverGrid而不是home.dart

因为当您使用GridView.builder时,它将同时加载所有可能的结果,而不是使用SliverGrid时,它将只加载当前存在于screen中的数据。

代码语言:javascript
运行
复制
SliverGrid(delegate: SliverChildBuilderDelegate((context, int index) {
          final _wallpaper = wallpaper.photos ? [index];
          return Container(decoration: BoxDecoration(borderRadius: BorderRadius.circular(10.0),), height: MediaQuery
              .of(context)
              .size
              .height / 2, child: GestureDetector(onTap: () {
            //Wallpaper Preview
          },
            child: Hero(tag: '${_wallpaper?.id}',
              child: ClipRRect(borderRadius: BorderRadius.circular(10.0),
                child: CachedNetworkImage(fit: BoxFit.cover,
                  imageUrl: _wallpaper!.src!.large!,
                  filterQuality: FilterQuality.high,
                  progressIndicatorBuilder: (context, url, progress) => Center(child: CircularProgressIndicator()),),),),),);
        }, childCount: wallpaper.photos?.length),
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 0.5, crossAxisSpacing: 5.0 mainAxisSpacing: 5.0,),),

注意:去掉你不需要的额外的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64405266

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档