如何仅重新加载从 PageView 中的 URL 检索到的图像

问题描述 投票:0回答:1

我是颤振新手。 我想创建一个屏幕来查看从多个监控摄像头获取的图像。我使用

PageView
通过滑动来切换相机图像,并且还在 PageView 外部放置了一个按钮来重新加载图像。当按下此按钮时,我只想重新加载正在显示的相机图像。相机图像的上方和下方有不同的图像,但这些不需要重新加载。

如果我将

setState()
放在重新加载按钮的 OnTap 中,我可以重新加载它,但 PageView 会闪烁。是否可以仅重新加载图像?或者还有别的办法吗?谢谢。

main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'src/app.dart';
void main() {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
  ]);
  runApp(
    const MyApp()
  );
}

src/app.dart

import 'package:flutter/material.dart';
import 'screens/cam.dart';

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      //debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      home: BasePage(),
    );
  }
}

class BasePage extends StatefulWidget{
  @override
  _BasePage createState() => _BasePage();
}

class _BasePage extends State<BasePage> {
  final _pageController = PageController();
  int _currentIndex = 0;

  void changeIndex(index){
    setState(() {
      _currentIndex = index;
      _pageController.jumpToPage(index);
    });
  }
  
  @override
  Widget build(BuildContext context) {
    var TabItem = {
    //Omit unnecessary codes.
      'cam':[3, Icons.video_camera_back, Colors.lightGreen, CamScreen()],
    };

    return Scaffold(
      body: PageView(
        controller: _pageController,
        physics: const NeverScrollableScrollPhysics(),
        children: TabItem.values.map((value) => value[3] as Widget).toList(),
      ),
      bottomNavigationBar: SizedBox(
        height: MediaQuery.of(context).size.height /8,
        child:BottomNavigationBar(
          //Omit unnecessary codes.         
        ),
      ),
    );
  }
}

src/screens/cam.dart

import 'package:flutter/material.dart';
import 'dart:math';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';

class CamScreen extends StatefulWidget {
  @override
  _CamScreen createState() => _CamScreen();
}

class _CamScreen extends State<CamScreen>{
  Future<Map<String, dynamic>> fetchData(int camNumber) async {
    final response = await http.get(Uri.parse('https://mydomain/get_cam_info?no=$camNumber'));
    if (response.statusCode == 200) {
      final jsonData = jsonDecode(response.body);
      return jsonData;
    } else {
      throw Exception('Failed to load data');
    }
  }

  @override
  Widget build(BuildContext context) {
    const camCount = 5;
    final _pageController = PageController(initialPage: camCount * 5);

    return Column(
      children: [
        Expanded(
          flex: 7,
          child: PageView.builder(
            controller: _pageController,
            itemBuilder: (context, index){
              return FutureBuilder(
                future: fetchData(index % camCount + 1), 
                builder: (context, snapshot) {
                  if(snapshot.connectionState == ConnectionState.waiting){
                    return const Center(child: CircularProgressIndicator());
                  } else if(snapshot.hasError || snapshot.data == null){
                    return Column(children: [Expanded(child:
                      Image.network('https://mydomain/cam_img?no=${index % camCount +1}&cache=${Random().nextInt(100)}',)
                    )]);
                  } else {
                    final jsonData = snapshot.data!;
                    final camName = jsonData['name'];
                    final upperPath = jsonData['upper']['path'];
                    final upperUrl = jsonData['upper']['url'];
                    final lowerPath = jsonData['lower']['path'];
                    final lowerUrl = jsonData['lower']['url'];

                    return Scaffold(
                      backgroundColor: Colors.black,
                      appBar: AppBar(
                        toolbarHeight: MediaQuery.of(context).size.height /16,
                        title: Text(camName),
                        centerTitle: true,
                        backgroundColor: Colors.orange,
                      ),

                      body: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          if (upperUrl != null && upperUrl.isNotEmpty && upperPath != null && upperPath.isNotEmpty)
                            Expanded(child: GestureDetector(
                              onTap: () async { await launchUrl(Uri.parse(upperUrl)); },
                              child: Image.network('https://mydomain/img/$upperPath'),
                            ),),

                          SizedBox(
                            width: MediaQuery.of(context).size.width,
                            child:Image.network('https://mydomain/cam_img?no=${index % camCount +1}&cache=${Random().nextInt(10000)}',)
                          ),
                          
                          if (lowerUrl != null && lowerUrl.isNotEmpty && lowerPath != null && lowerPath.isNotEmpty)
                            Expanded(child:GestureDetector(
                              onTap: () async { await launchUrl(Uri.parse(lowerUrl)); },
                              child: Image.network('https://mydomain/img/$lowerPath'),
                            ),)
                        ],
                      ),
                    );
                  }
                }
              );
            },
          ),
        ),

        Expanded(
          flex: 1,
          child: Row(
            children: [
              Expanded(
                child: GestureDetector(
                  onTap: (){},
                  child:  Container(
                    decoration: BoxDecoration(
                      color: Colors.indigo.shade300,
                      border: Border.all(width: 2, color: Colors.white),
                    ),                    
                    child: const Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Icon(Icons.location_on, size: 40,),
                        Text('map')
                      ],
                    )
                  )
                ),
              ),

              Expanded(
                child: GestureDetector(
                  onTap: (){
                    setState(() {
                    });
                  },
                  child:  Container(
                    decoration: BoxDecoration(
                      color: Colors.indigo.shade300,
                      border: Border.all(width: 2, color: Colors.white),
                    ),
                    child: const Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Icon(Icons.refresh, size: 35,),
                        Text('reload')
                      ],
                    )
                  )
                ),
              ),
            ],
          )
        )
        
      ],
    );
  }
}
flutter flutter-image flutter-pageview
1个回答
0
投票

这可以通过将 Image.network 分离到一个单独的小部件中来实现。

cam.dart

import 'dart:convert';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';


class CamScreen extends StatefulWidget {
  @override
  _CamScreen createState() => _CamScreen();
}

class _CamScreen extends State<CamScreen> {
  Future<Map<String, dynamic>> fetchData(int camNumber) async {
    final response = await http.get(Uri.parse('https://mydomain/get_cam_info?no=$camNumber'));
    if (response.statusCode == 200) {
      return jsonDecode(response.body);
    } else {
      throw Exception('Failed to load data');
    }
  }

  @override
  Widget build(BuildContext context) {
    const camCount = 5;
    final _pageController = PageController(initialPage: camCount * 5);
    final List<GlobalKey<_ReloadableImageState>> _keys = List.generate(camCount*10, (index) => GlobalKey<_ReloadableImageState>());

    return Column(
      children: [
        Expanded(
          flex: 7,
          child: PageView.builder(
            controller: _pageController,
            itemBuilder: (context, index) {
              return FutureBuilder(
                future: fetchData(index % camCount + 1), 
                builder: (context, snapshot) {
                  if(snapshot.connectionState == ConnectionState.waiting){
                    return const Column(children: [Expanded(child: CircularProgressIndicator())]);
                  } else if(snapshot.hasError || snapshot.data == null){
                    return Column(children: [Expanded(child:
                      ReloadableImage(key: _keys[index],camNumber: index % camCount + 1,)
                    )]);
                  } else {
                    final jsonData = snapshot.data!;
                    final camName = jsonData['name'];
                    final upperPath = jsonData['upper']['path'];
                    final upperUrl = jsonData['upper']['url'];
                    final lowerPath = jsonData['lower']['path'];
                    final lowerUrl = jsonData['lower']['url'];

                    return Scaffold(
                      backgroundColor: Colors.black,
                      appBar: AppBar(
                        toolbarHeight: MediaQuery.of(context).size.height /16,
                        title: Text(camName),
                        centerTitle: true,
                        backgroundColor: Colors.orange,
                      ),

                      body: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          if (upperUrl != null && upperUrl.isNotEmpty && upperPath != null && upperPath.isNotEmpty)
                            Expanded(child: GestureDetector(
                              onTap: () async { await launchUrl(Uri.parse(upperUrl)); },
                              child: Image.network('https://mydomain/img/$upperPath'),
                            ),),
                          
                          ReloadableImage(key: _keys[index],camNumber: index % camCount + 1),

                          if (lowerUrl != null && lowerUrl.isNotEmpty && lowerPath != null && lowerPath.isNotEmpty)
                            Expanded(child:GestureDetector(
                              onTap: () async { await launchUrl(Uri.parse(lowerUrl)); },
                              child: Image.network('https://mydomain/img/$lowerPath'),
                            ),)
                        ],
                      ),
                    );
                  }
                }
              );
            },
          ),
        ),
        
        Expanded(
          flex: 1,
          child: GestureDetector(
            onTap: (){
              final currentPage = _pageController.page?.round() ?? 0;
              _keys[currentPage].currentState?.reloadImage();
            },
            child: Container(
              decoration: BoxDecoration(
                color: Colors.indigo.shade300,
                border: Border.all(width: 2, color: Colors.white),
              ),
              child: const Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(Icons.refresh, size: 35,),
                  Text('reload')
                ],
              )
            )
          ),
        ),
      ],
    );
  }
}

class ReloadableImage extends StatefulWidget {
  final int camNumber;
  final GlobalKey<_ReloadableImageState> key;
  ReloadableImage({required this.camNumber, required this.key}) : super(key: key);

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

class _ReloadableImageState extends State<ReloadableImage> {
  void reloadImage() {
    setState(() {
    });
  }
  @override
  Widget build(BuildContext context) {
    return Image.network('https://mydomain/cam_img?no=${widget.camNumber}&cache=${Random().nextInt(10000)}');
  }
}



© www.soinside.com 2019 - 2024. All rights reserved.