प्रोग्राम सूची के अंत में स्क्रॉल करना


108

मेरे पास एक स्क्रॉल करने योग्य ListViewआइटम है जहां वस्तुओं की संख्या गतिशील रूप से बदल सकती है। जब भी सूची के अंत में एक नया आइटम जोड़ा जाता है, तो मैं प्रोग्रामेटिक रूप ListViewसे अंत तक स्क्रॉल करना चाहूंगा । (उदाहरण के लिए, चैट संदेश सूची जैसी कोई चीज़ जहां नए संदेश अंत में जोड़े जा सकते हैं)

मेरा अनुमान है कि मुझे ScrollControllerअपनी Stateवस्तु बनाने और इसे मैन्युअल रूप से ListViewकंस्ट्रक्टर में भेजने की आवश्यकता होगी, इसलिए मैं बाद में नियंत्रक पर कॉल animateTo()/ jumpTo()विधि कर सकता हूं । हालाँकि, चूंकि मैं अधिकतम स्क्रॉल ऑफ़सेट को आसानी से निर्धारित नहीं कर सकता, इसलिए बस एक scrollToEnd()प्रकार का ऑपरेशन करना असंभव लगता है (जबकि मैं 0.0इसे प्रारंभिक स्थिति में स्क्रॉल करने के लिए आसानी से पास कर सकता हूं )।

क्या इसे प्राप्त करने का एक आसान तरीका है?

का उपयोग करना reverse: trueमेरे लिए एक सही समाधान नहीं है, क्योंकि मैं चाहूंगा कि आइटम शीर्ष पर गठबंधन किए जाएं जब ListViewव्यूपोर्ट के भीतर केवल कुछ ही आइटम फिट होते हैं ।

जवाबों:


106

यदि आप एक सिकुड़ते-लपेटते का उपयोग ListViewकरते हैं reverse: true, तो इसे 0.0 पर स्क्रॉल करना है जो आप चाहते हैं।

import 'dart:collection';

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Example',
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<Widget> _messages = <Widget>[new Text('hello'), new Text('world')];
  ScrollController _scrollController = new ScrollController();

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Center(
        child: new Container(
          decoration: new BoxDecoration(backgroundColor: Colors.blueGrey.shade100),
          width: 100.0,
          height: 100.0,
          child: new Column(
            children: [
              new Flexible(
                child: new ListView(
                  controller: _scrollController,
                  reverse: true,
                  shrinkWrap: true,
                  children: new UnmodifiableListView(_messages),
                ),
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        child: new Icon(Icons.add),
        onPressed: () {
          setState(() {
            _messages.insert(0, new Text("message ${_messages.length}"));
          });
          _scrollController.animateTo(
            0.0,
            curve: Curves.easeOut,
            duration: const Duration(milliseconds: 300),
          );
        }
      ),
    );
  }
}

1
यह चाल चली। अपने विशिष्ट मामले में मुझे एक विस्तृत विजेट में ListView को डालने के लिए नेस्टेड कॉलम का उपयोग करना था, लेकिन मूल विचार एक ही है।
ययून

21
मैं एक समाधान की तलाश में आया था, बस यह उल्लेख करना चाहता था कि अन्य उत्तर इसे प्राप्त करने का एक बेहतर तरीका हो सकता है - stackoverflow.com/questions/44141148/…
अनिमेष जैन

6
मैं सहमत हूं, वह उत्तर (जो मैंने भी लिखा था) निश्चित रूप से बेहतर है।
कॉलिन जैक्सन

1
@ डेनिस ताकि सूची दृश्य उल्टे क्रम में शुरू हो जो नीचे से है।
CopsOnRoad

1
@CopsOnRoad से उत्तर बेहतर दिखता है और यह उत्तर समाधान की तुलना में हैक की तरह दिखता है।
रूपक ए नेलिएट

128

इसे प्राप्त करने के लिए उपयोग ScrollController.jumpTo()या ScrollController.animateTo()विधि।

उदाहरण:

final _controller = ScrollController();

@override
Widget build(BuildContext context) {
  
  // After 1 second, it takes you to the bottom of the ListView
  Timer(
    Duration(seconds: 1),
    () => _controller.jumpTo(_controller.position.maxScrollExtent),
  );

  return ListView.builder(
    controller: _controller,
    itemCount: 50,
    itemBuilder: (_, __) => ListTile(title: Text('ListTile')),
  );
}

यदि आप चिकनी स्क्रॉलिंग चाहते हैं, तो इसके बजाय jumpToउपरोक्त उपयोग करने के लिए

_controller.animateTo(
  _controller.position.maxScrollExtent,
  duration: Duration(seconds: 1),
  curve: Curves.fastOutSlowIn,
);

4
Firebase रीयलटाइम डेटाबेस के लिए, मैं अब तक 250ms (जो शायद वास्तव में लंबा है) के साथ दूर होने में सक्षम हूं। मुझे आश्चर्य है कि हम कितने कम जा सकते हैं? समस्या यह है कि नए आइटम के साथ समाप्त होने के लिए विजेट निर्माण के लिए इंतजार करने की जरूरत है। पूर्णता बनाने के लिए कॉलबैक से औसत अवधि कैसे प्रोफाइल करें?
SacWebDeveloper

2
क्या आपको लगता है कि फायरबेस से डेटा प्राप्त करने के लिए स्ट्रीमबिल्डर का उपयोग करने के साथ यह ठीक से काम करेगा? साथ ही सेंड बटन पर चैट मैसेज भी जोड़ रहे हैं? इसके अलावा अगर इंटरनेट कनेक्शन धीमा है तो यह काम करेगा? अगर आप सुझाव दे सकते हैं तो बेहतर है। धन्यवाद।
बजे जे मुंगारा

@SacWebDeveloper इस मुद्दे के साथ आपका दृष्टिकोण क्या था और साथ ही फायरबेस?
इदरीस स्टैक

@IdrisStack वास्तव में, 250ms जाहिर तौर पर लंबे समय के रूप में कभी-कभी नहीं होते हैं, कूदता है अपडेट होने से पहले। मुझे लगता है कि अद्यतन के बाद सूची की लंबाई की तुलना करने के लिए बेहतर दृष्टिकोण होगा और यदि नीचे की लंबाई एक से अधिक हो तो कूदें।
SacWebDeveloper

धन्यवाद @SacWebDeveloper, आपका दृष्टिकोण काम करता है, हो सकता है कि मैं आपसे फायरस्टार के संदर्भ में एक और सवाल पूछूं, यह विषय को रद्द कर सकता है। Qn। ऐसा क्यों है कि जब मैं किसी को संदेश भेजता हूं तो सूची स्वचालित रूप से नीचे स्क्रॉल नहीं होती है, क्या संदेश सुनने और नीचे स्क्रॉल करने का कोई तरीका है?
इदरीस स्टैक

14

listViewScrollController.animateTo(listViewScrollController.position.maxScrollExtent) सबसे सरल तरीका है।


2
हाय जैक, आपके उत्तर के लिए धन्यवाद, मेरा मानना ​​है कि मैंने वही लिखा था, इसलिए IMHO को फिर से एक ही समाधान जोड़ने का कोई फायदा नहीं है।
CopsOnRoad

1
मैं इस दृष्टिकोण का उपयोग कर रहा था, लेकिन आपको इसे करने के लिए कुछ ms की प्रतीक्षा करनी होगी क्योंकि चेतन को चलाने से पहले स्क्रीन को प्रस्तुत करना होगा (...)। हो सकता है कि हम बिना किसी हैक के इसे करने के लिए अच्छी प्रथाओं का उपयोग कर रहे हों।
रेनन कोल्हो

1
यही सही जवाब है। यदि आप वर्तमान लाइन में +100 हैं तो यह आपकी सूची को नीचे तक स्क्रॉल करेगा। listViewScrollController.position.maxScrollExtent + 100 की तरह;
रिजवान अंसार

1
@ रेनानचेलो से प्रेरणा लेकर, मैंने 100 मिलीसेकंड की देरी से एक टाइमर में कोड की इस पंक्ति को लपेटा।
23

5

सही परिणाम पाने के लिए मैंने कॉलिन जैक्सन और कोप्सऑनराड के उत्तरों को निम्न प्रकार से संयोजित किया:

_scrollController.animateTo(
                              _scrollController.position.maxScrollExtent,
                              curve: Curves.easeOut,
                              duration: const Duration(milliseconds: 500),
                            );

2

मुझे सूची के निचले भाग में जाने के लिए स्क्रॉल नियंत्रक का उपयोग करने की कोशिश करने में इतनी समस्या है कि मैं दूसरे दृष्टिकोण का उपयोग करता हूं।

सूची को नीचे तक भेजने के लिए एक घटना बनाने के बजाय, मैं उलट सूची का उपयोग करने के लिए अपने तर्क को बदल देता हूं।

इसलिए, हर बार मेरे पास एक नया आइटम होता है, मैं बस सूची के शीर्ष पर सम्मिलित किया जाता हूं।

// add new message at the begin of the list 
list.insert(0, message);
// ...

// pull items from the database
list = await bean.getAllReversed(); // basically a method that applies a descendent order

// I remove the scroll controller
new Flexible(
  child: new ListView.builder(
    reverse: true, 
    key: new Key(model.count().toString()),
    itemCount: model.count(),
    itemBuilder: (context, i) => ChatItem.displayMessage(model.getItem(i))
  ),
),

2
एकमात्र समस्या जो मुझे इसके साथ हो रही है वह यह है कि कोई भी स्क्रॉल बार अब पीछे की ओर काम करता है। जब मैं नीचे स्क्रॉल करता हूं, तो वे ऊपर चले जाते हैं।
थिंकडिजिटल

2

विजेटबाइंड को इनस्टैट में न रखें, इसके बजाय, आपको इसे उस विधि में रखना होगा जो आपके डेटा को डेटाबेस से लाती है। उदाहरण के लिए, इस तरह। यदि कोई गलती से लगा दिया जाता है, तो scrollcontrollerकिसी भी सूची में संलग्न नहीं होगा।

    Future<List<Message>> fetchMessage() async {

    var res = await Api().getData("message");
    var body = json.decode(res.body);
    if (res.statusCode == 200) {
      List<Message> messages = [];
      var count=0;
      for (var u in body) {
        count++;
        Message message = Message.fromJson(u);
        messages.add(message);
      }
      WidgetsBinding.instance
          .addPostFrameCallback((_){
        if (_scrollController.hasClients) {
          _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
        }
      });
      return messages;
    } else {
      throw Exception('Failed to load album');
    }
   }

-2

आप इसका उपयोग कर सकते हैं जहां 0.09*heightसूची में एक पंक्ति की ऊंचाई है और _controllerइसे इस तरह परिभाषित किया गया है_controller = ScrollController();

(BuildContext context, int pos) {
    if(pos != 0) {
        _controller.animateTo(0.09 * height * (pos - 1), 
                              curve: Curves.easeInOut,
                              duration: Duration(milliseconds: 1400));
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.