Flutter滚动到Dynamic ListView的底部

时间:2017-12-21 00:31:59

标签: flutter

我看过并找到了几个解决方案,但似乎没有一个适合我的配置,需要一些帮助。我将把所有代码放在这里,看看是否有人知道在哪里应用ScrollController。我已尝试使用原始ListView,但我在futureView构建器中从futureResponse动态构建其他项目。

import 'dart:async';
import 'dart:io';
import 'dart:typed_data';


import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:cswauthapp/models.dart';
import 'package:cswauthapp/ChatConvoDetail.dart';
import 'package:cswauthapp/Settings.dart' as Admin;
import 'package:cswauthapp/HomePage.dart' as HomePage;
import 'package:cswauthapp/main.dart' as MyHomePage;
import 'package:cswauthapp/PostAuthHome.dart' as PostAuthHome;
import 'package:image_picker/image_picker.dart';
import 'package:cswauthapp/ShowPic.dart';
import 'package:path/path.dart' as path;
import 'package:video_player/video_player.dart';
import 'package:cswauthapp/vplayer.dart' as vplayer;

class ChatDivided extends StatefulWidget {
  ChatDivided({Key key, this.title, this.mychat}) : super(key: key);

  static const String routeName = "/ChatDivided";

  final ChatList mychat;
  final String title;

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

class _ChatDividedState extends State<ChatDivided> {
  SharedPreferences prefs;
  int oid = 0;
  int pid = 0;
  int authlevel = 0;
  bool admin = false;
  int type = 0;
  String msgid = '';
  List chatlist;
  int listcount = 0;
  bool grpmsg = true;
  String sender = '';
  String receiver = '';
  String message = '';
  String oname = '';
  String pname = '';
  String sendname;
  String receivename;
  String replyto = '';
  String replyfrom = '';
  String replysub = '';
  final TextEditingController _newreplycontroller = new TextEditingController();
  String myfcmtoken = 'NONE';
  Future<http.Response> _responseFuture;
  var _urlDates = '';
  Future<File> _imageFile;
  String myimage;
  String myvideo;
  File myimagefile;
  File myvidfile;
  Future<int> myimagelength;
  String myext;
  VideoPlayerController vcontroller;
  bool isImage = false;
  bool isVideo = false;
  //ScrollController scontroller = new ScrollController();

  _getPrefs() async {
    prefs = await SharedPreferences.getInstance();

    if (mounted) {
      setState(() {
        oid = prefs.getInt('oid');
        pid = prefs.getInt('pid');
        authlevel = prefs.getInt('authlevel');
        admin = prefs.getBool('admin');
        type = 1;
        msgid = widget.mychat.msgkey;
        if (widget.mychat.grpid == '0') {
          grpmsg = false;
        } else {
          grpmsg = true;
        }
        oname = widget.mychat.oname;
        pname = widget.mychat.pname;
        myfcmtoken = prefs.getString('fcmtoken');
        if (authlevel == 0) {
          sender = 'o';
          receiver = 'p';
          sendname = widget.mychat.oname;
          receivename = widget.mychat.pname;
        } else if (authlevel == 1) {
          sender = 'p';
          receiver = 'o';
          sendname = widget.mychat.pname;
          receivename = widget.mychat.oname;
        }
        //_getChats();
      });
    }
  }

  @override
  void initState() {
    super.initState();
    //controller = new TabController(length: 4, vsync: this);
    _getPrefs();
    _urlDates =
        'http://$baseurl/chat/messages/getdates/${widget
        .mychat.msgkey}';
    _responseFuture = http.get(_urlDates, headers: getAuthHeader());
  }

  var jsonCodec = const JsonCodec();
  var _focusnode = new FocusNode();


  _getChats() async {

    var _url =
        'http://$baseurl/chat/messages/getdates/$msgid';

    var http = createHttpClient();
    var response = await http.get(_url, headers: getAuthHeader());

    var chats = await jsonCodec.decode(response.body);

    if (mounted) {
      setState(() {
        chatlist = chats.toList();
        listcount = chatlist.length;
        //replysub = 'Re: ' + chatlist[0]['sub'];
      });
    }
  }

  Future<Null> _onRefresh() {
    Completer<Null> completer = new Completer<Null>();
    Timer timer = new Timer(new Duration(seconds: 1), () {
      setState(() {
        _responseFuture = http.get(_urlDates, headers: getAuthHeader());
        print('RUNNING LOAD AFTER REFRESH AGAIN');
      });
      completer.complete();
    });
    return completer.future;
  }

  Future<String> doImageString() async {

    return (await _imageFile).path.substring((await _imageFile).path.length - 3);
  }

  @override
  Widget build(BuildContext context) {
    Widget mytitle;
    if (grpmsg) {
      mytitle = new Row(
        mainAxisAlignment: MainAxisAlignment.start,
        children: <Widget>[
          new Icon(Icons.people),
          new Text('  '),
          new Text(widget.mychat.referralname)
        ],
      );
    } else {
      mytitle = new Row(
        mainAxisAlignment: MainAxisAlignment.start,
        children: <Widget>[
          new Icon(Icons.person),
          new Text('  '),
          new Text(widget.mychat.referralname)
        ],
      );
    }

    var _children = <Widget>[
      new Flexible(
        child: new RefreshIndicator(
            child: new FutureBuilder(
              future: _responseFuture,
              builder: (BuildContext context,
                  AsyncSnapshot<http.Response> response) {
                if (!response.hasData) {
                  return const Center(
                    //child: const Text('Loading Dates...'),
                    child: const CircularProgressIndicator(),
                  );
                } else if (response.data.statusCode != 200) {
                  return const Center(
                    child: const Text('Error loading data'),
                  );
                } else {
                  List<dynamic> json = JSON.decode(response.data.body);
                  return new MyChatList(json);
                }
              },
            ),
            onRefresh: _onRefresh),
      ),
      new Container(
          alignment: Alignment.bottomLeft,
          padding: new EdgeInsets.only(left: 10.0),
          child: new FutureBuilder<File>(
              future: _imageFile,
              builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
                if (snapshot.connectionState == ConnectionState.done) {
                  //return new Image.file(snapshot.data);
                  myimagefile = snapshot.data;
                  myext = path.extension(myimagefile.path);
                  if (myext == '.jpg') {
                    isImage = true;
                    return new Column(
                      mainAxisAlignment: MainAxisAlignment.start,
                      children: <Widget>[
                        new Container(
                          alignment: Alignment.bottomLeft,
                          width: 150.0,
                          child: new Image.file(snapshot.data),
                        ),
                        new FlatButton(
                            onPressed: _doClear, child: new Text('Clear Image'))
                      ],
                    );
                  } else {
                    isVideo = true;
                    myvidfile = new File(snapshot.data.path.replaceAll('file://', ''));
                    vcontroller = new VideoPlayerController(myimagefile.path)..initialize();
                    return new Column(
                      mainAxisAlignment: MainAxisAlignment.start,
                      children: <Widget>[
                        new Container(
                          alignment: Alignment.bottomLeft,
                          width: 300.0,
                          child: new vplayer.VideoCard(controller: vcontroller, title: widget.mychat.referralname,subtitle: 'Video',),
                        ),
                        new FlatButton(
                            onPressed: _doClear, child: new Text('Clear Video'))
                      ],
                    );
                  }

                } else {
                  return const Text('');
                }
              })
      ),
      new Divider(
        height: 5.0,
        color: Colors.grey,
      ),
      new Row(
        crossAxisAlignment: CrossAxisAlignment.end,
        children: <Widget>[
          new Container(
            alignment: Alignment.bottomLeft,
            //width: 50.0,
            child: new IconButton(
              icon: new Icon(Icons.add_a_photo),
              onPressed: _pickImage,
              alignment: Alignment.bottomLeft,
            ),
          ),
          new Flexible(
            child: new Container(
              alignment: Alignment.center,
              //width: 350.0,
              child: new TextField(
                decoration: const InputDecoration(
                  hintText: 'Reply',
                  labelText: 'Reply:',
                ),
                autofocus: false,
                focusNode: _focusnode,
                maxLines: 1,
                controller: _newreplycontroller,
                keyboardType: TextInputType.text,
              ),
            ),
          ),
          new Container(
              alignment: Alignment.bottomRight,
              //width: 50.0,
              child: new IconButton(
                icon: new Icon(Icons.send),
                onPressed: _sendReply,
                alignment: Alignment.centerRight,
                disabledColor: Colors.grey,
              )),
        ],
      ),
    ];

    return new Scaffold(
      appBar: new AppBar(
        title: mytitle,
        actions: getAppBarActions(context),
      ),
      body: new Column(
        children: _children,
      ),
    );
  }

  DateTime getDateDiv(int index) {
    DateTime msgdate = DateTime.parse(chatlist[index]['chatdate']).toLocal();
    return msgdate;
  }

  _doClear() {
    setState(() {
      _imageFile = null;
    });
  }

  _pickImage() async {
    await setState(()  {
      _imageFile = ImagePicker.pickImage(maxWidth: 600.0);
    });
  }

  _sendReply() {
    if (_newreplycontroller.text.isEmpty && myimagefile == null) {
      showDialog(
        context: context,
        child: new AlertDialog(
          content: new Text("There is no message to submit"),
          actions: <Widget>[
            new FlatButton(
                child: const Text('OK'),
                onPressed: () {
                  Navigator.pop(context, false);
                }),
          ],
        ),
      );
    } else {
      TextInputAction.done;
      DateTime dateSubmit = new DateTime.now();
      if (myimagefile != null) {
        if (isImage) {
          List<int> imageBytes = myimagefile.readAsBytesSync();
          myimage = BASE64.encode(imageBytes);
        }
        if (isVideo) {
          List<int> imageBytes = myvidfile.readAsBytesSync();
          myvideo = BASE64.encode(imageBytes);
        }

      } else {
        myimage = '';
        myvideo = '';
      }
      var mymessage = _newreplycontroller.text;
      ChatMessage mychat = new ChatMessage(
          widget.mychat.msgkey,
          widget.mychat.referralname,
          replysub,
          oid,
          oname,
          pid,
          pname,
          sender,
          sendname,
          receiver,
          receivename,
          mymessage,
          dateSubmit.toString(),
          widget.mychat.grpid,
          widget.mychat.prid,
          myfcmtoken,
          myimage,
          myvideo,
          myext
      );
      _doSendReply(mychat);
    }
  }

  _doSendReply(mychat) async {
    var json = jsonCodec.encode(mychat);
    var _url = 'http://$baseurl/chat/messages/send';

    //var request = new http.MultipartRequest('POST', _url)
    var http = createHttpClient();
    var response = await http.post(_url, body: json, headers: getJSONHeader());

    var chatresp = await jsonCodec.decode(response.body);
    if (chatresp.contains('GOOD')) {
      setState(() {
        _responseFuture = http.get(_urlDates, headers: getAuthHeader());
        _doClear();
        print('RUNNING LOAD AFTER SEND AGAIN');
      });
      _newreplycontroller.text = '';
      _focusnode.unfocus();
    } else if (chatresp.contains('EMPTY')) {
      showDialog(
        context: context,
        child: new AlertDialog(
          content: new Text("There is no message to submit"),
          actions: <Widget>[
            new FlatButton(
                child: const Text('OK'),
                onPressed: () {
                  Navigator.pop(context, false);
                }),
          ],
        ),
      );
    } else {}
  }
}

class MyChatList extends StatelessWidget {
  final List<dynamic> elementList;
  static ScrollController _scrollController;

  MyChatList(this.elementList);

  List<Widget> _getChildren() {
    List<Widget> children = [];
    elementList.forEach((element) {
      children.add(
        new MyChatWidget(
            datediv: element['msgdate'], msgkey: element['msgkey']),
      );
      //_scrollController.animateTo(0.0, duration: const Duration(milliseconds: 300), curve: Curves.easeOut);
    });
    return children;
  }

  @override
  Widget build(BuildContext context) {
    //_scrollController.position.maxScrollExtent;
    return new ListView(
      shrinkWrap: true,
      controller: _scrollController,
      reverse: false,
      children: _getChildren(),
    );

  }
}

class MyChatWidget extends StatefulWidget {
  MyChatWidget({Key key, this.datediv, this.msgkey}) : super(key: key);

  final String datediv;
  final String msgkey;

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

class _MyChatWidgetState extends State<MyChatWidget> {
  List<Widget> messagelist;
  int messagecount = 0;
  var jsonCodec = const JsonCodec();
  var mydate = '';
  var _urlMessages = '';
  PageStorageKey _key;
  Future<http.Response> _responseFuture;
  List messList;
  var mybytes;
  File myimageview;
  Image newimageview;
  String imgStr;
  String vidStr;

  @override
  void initState() {
    super.initState();
    if (new DateFormat.yMd().format(DateTime.parse(widget.datediv)) ==
        new DateFormat.yMd().format(new DateTime.now())) {
      mydate = 'Today';
    } else {
      mydate = new DateFormat.yMMMEd().format(DateTime.parse(widget.datediv));
    }
    DateChatMessage dcm =
        new DateChatMessage(widget.msgkey, widget.datediv.toString());
    var json = jsonCodec.encode(dcm);
    _urlMessages =
        'http://loop-dev.clinicalsoftworks.com/chat/messages/getbydate';
    _responseFuture =
        http.post(_urlMessages, body: json, headers: getAuthHeader());

    //controller = new TabController(length: 4, vsync: this);
    //_getMessages();
  }

  @override
  Widget build(BuildContext context) {
    _key = new PageStorageKey('${widget.datediv.toString()}');
    VideoPlayerController vcontroller;
    return new Column(
      children: <Widget>[
        new Container(
          child: new Text(
            mydate,
            textAlign: TextAlign.left,
            style: new TextStyle(
              color: Colors.grey,
              fontWeight: FontWeight.bold,
            ),
          ),
          alignment: Alignment.centerLeft,
          padding: new EdgeInsets.only(left: 10.0),
        ),
        new Container(
          child: new Divider(
            height: 5.0,
            color: Colors.grey,
          ),
          padding: new EdgeInsets.only(left: 10.0, right: 10.0),
        ),
        /**/
        new FutureBuilder(
          future: _responseFuture,
          builder:
              (BuildContext context, AsyncSnapshot<http.Response> response) {
            if (!response.hasData) {
              return const Center(
                child: const Text('Loading Messages...'),
              );
            } else if (response.data.statusCode != 200) {
              return const Center(
                child: const Text('Error loading data'),
              );
            } else {
              List<dynamic> json = JSON.decode(response.data.body);
              messagelist = [];
              json.forEach((element) {
                DateTime submitdate =
                    DateTime.parse(element['submitdate']).toLocal();
                String myvideo = element['chatvideo'];
                String myimage = element['chatimage'];
                if (myimage != null) {
                  imgStr = 'http://loop-dev.clinicalsoftworks.com/chat/getimage/'+element['chatimage'];

                } else if (myvideo != null) {
                  vidStr = 'http://loop-dev.clinicalsoftworks.com/chatuploads/'+element['chatvideo'];
                  vcontroller = new VideoPlayerController(vidStr)..initialize();
                }
                _showLgPic() {
                  Route route = new MaterialPageRoute(
                    settings: new RouteSettings(name: "/ShowPic"),
                    builder: (BuildContext context) => new ShowPic(
                          image: imgStr,
                        ),
                  );
                  Navigator.of(context).push(route);
                }

                _addImage() {
                  //assert(imgStr != null);
                  //myimageview = new Image.memory(mbytes);
                  new GestureDetector(
                    /*child: new Image(
                      image: newimageview.image,
                      width: 300.0,
                    ),*/
                    child: new Image.network(imgStr),
                    onTap: _showLgPic,
                  );
                }

                _addNoImage() {
                  assert(imgStr == null);
                  new Text('');
                }

                _showAssets() {
                  if (imgStr != null) {
                    new GestureDetector(
                      child: new Image.network(
                        imgStr,
                        width: 300.0,
                      ),
                      onTap: _showLgPic,
                    );
                  } else if (vidStr != null) {
                    new vplayer.VideoCard(controller: vcontroller,title: element['referralname'],subtitle: 'video',);
                  } else {
                    new Container();
                  }
                }

                messagelist.add(
                  new Container(
                    //width: 300.0,
                    padding: new EdgeInsets.all(10.0),
                    child: new Column(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      crossAxisAlignment: CrossAxisAlignment.stretch,
                      mainAxisSize: MainAxisSize.min,
                      children: <Widget>[
                        new Container(
                          padding: new EdgeInsets.only(bottom: 5.0),
                          child: new Row(
                            mainAxisSize: MainAxisSize.min,
                            children: <Widget>[
                              new CircleAvatar(
                                child: new Text(
                                  element['sendname'][0],
                                  style: new TextStyle(fontSize: 15.0),
                                ),
                                radius: 12.0,
                              ),
                              new Text('    '),
                              new Text(
                                element['sendname'],
                                style: new TextStyle(
                                    fontSize: 15.0,
                                    fontWeight: FontWeight.bold),
                              ),
                              new Text('    '),
                              new Text(
                                new DateFormat.Hm().format(submitdate),
                                style: new TextStyle(
                                    color: Colors.grey, fontSize: 12.0),
                              ),
                              //new Text(submitdate.toLocal().toString())
                            ],
                          ),
                        ),

                        new Row(
                          children: <Widget>[
                            new Text('          '),
                            new Flexible(
                              child: new Text('${element['message']}'),
                            )
                          ],
                        ),
                        new Container(
                            width: 300.0,
                            child: new Row(
                              children: <Widget>[
                                new Text('          '),
                                //_showAssets(),
                                imgStr != null
                                    ? new GestureDetector(
                                        child: new Image.network(
                                          imgStr,
                                          width: 300.0,
                                        ),
                                        onTap: _showLgPic,
                                      )
                                    : vidStr != null
                                    ? new Flexible(child: new vplayer.VideoCard(controller: vcontroller,title: element['referralname'],subtitle: 'video',),)
                                    : new Container(),
                              ],
                            )
                        ),
                      ],
                    ),
                  ),
                );
              });
              return new Column(children: messagelist);
            }
          },
        ),
      ],
    );
  }
}

非常感谢任何帮助。

3 个答案:

答案 0 :(得分:1)

要滚动动态ListView的底部,请执行以下操作

ScrollController _scrollController = new ScrollController();

然后

ListView.builder(
    controller: _scrollController,
    itemCount: list.lenght,
    itemBuilder: (BuildContext ctxt, int index) {
        return Text("GMF ${list[index]}");
    }
)

最后

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

答案 1 :(得分:1)

我的解决方案...在ListView内添加:

reverse: true,
shrinkWrap: true,

并在我的列表中:

listModel = List.from(listModel.reversed);

答案 2 :(得分:0)

实现它的最简单方法是将reverse的{​​{1}}属性设置为true,然后使用控制器滚动到ListView

0.0

然后

ListView(
  shrinkWrap: true,
  controller: _scrollController,
  reverse: true, \\ <- set this true
  children: _getChildren(),
);