簡體   English   中英

Flutter:如何以編程方式和定期滾動(從流中)?

[英]Flutter: How scroll programmatically and periodic(from stream)?

我有 flutter 小部件。 它是 SingleChildScrollView 與孩子+“中間光標”:

文本

我可以左右滾動這個列表 - cursor 始終保持在中間。 現在我需要以一秒的頻率以編程方式滾動相對於 cursor 的“孩子”。

我嘗試添加一個計時器和一個線程並在我的小部件中處理它。 我還在我的小部件中添加了 SteamBuilder,但在一幀中出現錯誤。

我的錯誤框架:

在此處輸入圖像描述

我的正常框架:

在此處輸入圖像描述

此錯誤發生在一幀之后。 任何想法如何解決這個問題 - 你會非常感激。

這是我的完整代碼:

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

class StartApp extends StatefulWidget {
  @override
  _StartAppState createState() => _StartAppState();
}

class _StartAppState extends State<StartApp> {
  StreamController<double> _currentPositionController;
  double position = 0;
  Timer _progressTimer;

  void positionUpdate(Timer timer) {
    position += 0.550;
    _currentPositionController.add(position);
  }

  @override
  void initState() {
    super.initState();
    _progressTimer = Timer.periodic(
        Duration(milliseconds: 350), positionUpdate);
    _currentPositionController = StreamController<double>.broadcast();
  }

  @override
  void dispose() {
    _progressTimer?.cancel();
    _currentPositionController?.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: TmpPage(currentPosition:_currentPositionController.stream ),
      ),
    );
  }
}

這是我的小部件(TmpPage):

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

class TmpPage extends StatefulWidget {
  final Stream<double> currentPosition;

  const TmpPage({Key key, this.currentPosition}) : super(key: key);
  @override
  _TmpPageState createState() => _TmpPageState();
}

class _TmpPageState extends State<TmpPage> with WidgetsBindingObserver {

  double before = 0;
  double halfWidth;
  double leftPosition;
  ScrollController _controller;

  @override
  void initState() {

    WidgetsBinding.instance.addObserver(this);
    halfWidth =0;
    _controller = ScrollController();
    _controller.addListener(_scrollListener);
    super.initState();
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    _controller.removeListener(_scrollListener);
    _controller.dispose();
    super.dispose();
  }

  double width = 0.0;
  double height = 0.0;

  @override void didChangeMetrics() {
    setState(() {
      width = window.physicalSize.width;
      height = window.physicalSize.height;
      halfWidth = window.physicalSize.width / 2;
      //halfWidth = (MediaQuery.of(context).size.width) / 2;
      leftPosition = halfWidth;
    });
  }

  @override
  Widget build(BuildContext context) {
    return _getBody(context);
  }

  Widget _getBody(context) {
    if(halfWidth ==0)
      halfWidth = (MediaQuery.of(context).size.width) / 2;
    if (before == 0)
      leftPosition = halfWidth;
    return StreamBuilder<double>(
      stream: widget.currentPosition,
      builder: (context, snapshot) {
        if(snapshot.data !=null && _controller.hasClients){
            _controller.jumpTo(snapshot.data);
          }
        return NotificationListener<ScrollNotification>(
          onNotification: (scrollNotification) {
            if (scrollNotification is ScrollUpdateNotification) {
              var m = scrollNotification.metrics;
              before = m.extentBefore;
              setState(() {
                leftPosition = before;
              });
              return false;
            }
            return false;
          },
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              SingleChildScrollView(
                controller: _controller,
                  scrollDirection: Axis.horizontal,
                  child: Row(
                    children: <Widget>[
                      SizedBox(
                        width: halfWidth,
                      ),
                      Stack(
                        children: <Widget>[
                          Row(
                            children: List.generate(
                                30,
                                    (i) => Padding(
                                  padding: const EdgeInsets.all(2.0),
                                  child: new Container(
                                    height: 42.0,
                                    width: 42.0,
                                    color: _getColor(i),
                                  ),
                                )).toList(),
                          ),
                          Positioned(
                            left: leftPosition,
                            top: 0,
                            bottom: 0,
                            child: Container(
                              color: Colors.red,
                              width: 2,
                            ),
                          ),
                        ],
                      ),
                      SizedBox(
                        width: halfWidth,
                      ),
                    ],
                  )),
            ],
          ),
        );
      }
    );
  }

   Color _getColor(int i) {
    if(i<10)
      return Colors.blueGrey;
    if(i<20)
      return Colors.orange;
    if(i<30)
      return Colors.lightBlueAccent;
    else
      return Colors.green;

  }


  void _scrollListener() {
    if (_controller.offset >= _controller.position.maxScrollExtent &&
        !_controller.position.outOfRange) {
      print('end');
      _controller.jumpTo(_controller.offset - 1);
    }
    if (_controller.offset <= _controller.position.minScrollExtent &&
        !_controller.position.outOfRange) {
      print('start');
      _controller.jumpTo(_controller.offset + 1);
    }
  }
}

您不能從 build 方法中觸發構建,例如setState 因此,您可以將任何對setState的調用包裝為

WidgetsBinding.instance.addPostFrameCallback((_) {
  // Your code here
});

為了在構建方法完成后調用幀末尾的代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM