自定义滚动视图在银色应用栏中的文本框被点击时会自动滚动到顶部位置

9

开发了一个带有顶部搜索栏和底部水平和垂直列表视图的flutter页面...当我点击搜索栏文本框时,我的下方视图自动滚动到顶部位置。

当我调试它时,build方法再次调用,如何解决这个问题?

在这个例子中也发生了同样的问题https://github.com/lohanidamodar/flutter_ui_challenges,这是我参考的资料。

src \ pages \ hotel \ hhome.dart 是该页面

enter image description here

以下是我的代码,如果您觉得这段代码太长,请查看github示例代码(src \ pages \ hotel \ hhome.dart),该代码出现了相同的问题

class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return CustomScrollView(
  slivers: <Widget>[
    _buildSliverAppBar(),
    _buildTitle('Category'),
    _buildSliverCategoryList(),
    _buildSliverPopularProducts(),
    _buildTitle('Top Distributors'),
    _buildSliverTopDistributorsList(),
    _buildSliverClothing(),
    _buildSliverElectronics(),
    _buildSliverHealthAndBeauty(),
  ],
);
}

 //Appbar: with "search" & "filter"
Widget _buildSliverAppBar() {
return SliverAppBar(
  leading: Icon(null),
  backgroundColor: colorPrimary,
  elevation: 10.0,
  forceElevated: true,
  pinned: true,
  flexibleSpace: ListView(
    children: <Widget>[
      Padding(
        padding: const EdgeInsets.only(left: 16.0, right: 16.0),
        child: Row(
          children: <Widget>[
            Expanded(
              child: Container(
                margin: EdgeInsets.only(right: 8.0),
                alignment: Alignment.center,
                height: 38.0,
                padding: EdgeInsets.only(left: 16.0),
                decoration: BoxDecoration(
                    color: Colors.white,
                    borderRadius: BorderRadius.circular(40.0)),
                child: TextField(
                  style: TextStyle(fontSize: 15.0),
                  decoration: InputDecoration(
                    hintText: "Search products",
                    border: InputBorder.none,
                    suffixIcon: IconButton(
                      onPressed: () {},
                      color: colorBlack,
                      icon: Icon(Icons.search),
                      iconSize: 20,
                    ),
                  ),
                ),
              ),
              flex: 1,
            ),
            RawMaterialButton(
              splashColor: colorPrimary,
              constraints: BoxConstraints(maxWidth: 38.0),
              onPressed: () {},
              child: Icon(
                Icons.clear_all,
                color: colorBlack,
                size: 24.0,
              ),
              shape: CircleBorder(),
              fillColor: colorWhite,
              padding: EdgeInsets.all(7.0),
            )
          ],
        ),
      )
    ],
  ),
);
}

//get title for the list
Widget _buildTitle(String title) {
return SliverToBoxAdapter(
  child: Container(
    color: colorGrey,
    child: Padding(
      padding: EdgeInsets.only(top: 8.0, left: 16.0),
      child: Align(alignment: Alignment.centerLeft, child: Text(title)),
    ),
  ),
);
}

//get "category" list
Widget _buildSliverCategoryList() {
return SliverToBoxAdapter(
  child: Container(
    color: colorGrey,
    height: 100,
    child: ListView(
      padding: EdgeInsets.only(right: 16.0),
      scrollDirection: Axis.horizontal,
      children: _listCategoryModel.map((itemCategory) {
        return _buildListTileCategory(itemCategory);
      }).toList(),
    ),
  ),
);
}

 //get "category" listTile
Widget _buildListTileCategory(CategoryModel itemCategory) {
return Padding(
  padding: const EdgeInsets.only(left: 16.0, top: 8.0, bottom: 8.0),
  child: Container(
      alignment: Alignment.center,
      width: 55,
      child: Material(
        color: Colors.transparent,
        child: InkWell(
          splashColor: colorPrimary,
          onTap: () {
            print('clicked: category ${itemCategory.name}');
          },
          child: Column(
            children: <Widget>[
              Card(
                elevation: 8.0,
                color: colorPrimary,
                child: Container(
                  width: 55,
                  height: 55,
                ),
              ),
              Padding(
                padding: const EdgeInsets.only(top: 8.0),
                child: Text(
                  itemCategory.name,
                  overflow: TextOverflow.ellipsis,
                  style: TextStyle(fontSize: 10.0),
                ),
              )
            ],
          ),
        ),
      )),
);
}

//get "popular products" two item
Widget _buildSliverPopularProducts() {
return SliverToBoxAdapter(
  child: Container(
    padding: EdgeInsets.all(16.0),
    color: colorWhite,
    child: Column(
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.only(bottom: 8.0),
          child: Row(
            children: <Widget>[
              Expanded(child: Text('Popular Products')),
              Text(
                'View all',
                style: TextStyle(color: colorPrimary),
              ),
            ],
          ),
        ), //text: "Popular Products" title
        Row(
          children: <Widget>[getItem(), getItem()],
        )
      ],
    ),
  ),
);
}

Widget getItem() {
return Expanded(
  child: Card(
    elevation: 4.0,
    color: colorGrey,
    child: Container(
      child: Column(
        children: <Widget>[
          Card(
            elevation: 0.0,
            color: colorWhite,
            child: Padding(
              padding: EdgeInsets.all(8.0),
              child: Container(
                width: (getScreenWidth(context) / 2) - 24,
                height: (getScreenWidth(context) / 2) - 24,
                child: Stack(
                  children: <Widget>[
                    Align(
                      child: Icon(
                        Icons.favorite,
                        color: colorPrimary,
                      ),
                      alignment: Alignment.topRight,
                    )
                  ],
                ),
              ),
            ),
          ),
          Padding(
            padding:
                const EdgeInsets.only(left: 4.0, right: 4.0, bottom: 8.0),
            child: Align(
              child: Text(
                'Default watch',
                style: TextStyle(
                  fontSize: 12.0,
                ),
              ),
              alignment: Alignment.centerLeft,
            ),
          ), //text: product name
          Padding(
            padding:
                const EdgeInsets.only(left: 4.0, right: 4.0, bottom: 8.0),
            child: Align(
              child: Text(
                "\$25.75",
                style: TextStyle(
                  color: colorPrimary,
                ),
              ),
              alignment: Alignment.centerLeft,
            ),
          ), //text: product price in dollar
          Padding(
            padding:
                const EdgeInsets.only(left: 8.0, right: 8.0, bottom: 8.0),
            child: Row(
              children: <Widget>[
                Expanded(
                  child: RawMaterialButton(
                    onPressed: () {},
                    child: Icon(
                      Icons.remove,
                      color: colorPrimary,
                      size: 16.0,
                    ),
                    shape: CircleBorder(),
                    elevation: 2.0,
                    fillColor: colorWhite,
                    padding: EdgeInsets.all(6.0),
                    splashColor: colorPrimary,
                  ),
                ), //button: decrease product count
                Expanded(
                  flex: 2,
                  child: Container(
                    margin: EdgeInsets.only(left: 8.0, right: 8.0),
                    child: AbsorbPointer(
                      //to avoid touch on the button with not like disable 
effect
                      absorbing: true,
                      child: RaisedButton(
                        color: colorWhite,
                        textColor: colorPrimary,
                        elevation: 1.0,
                        padding: EdgeInsets.only(top: 8.0, bottom: 8.0),
                        child: Text(
                          '0',
                          style: TextStyle(fontSize: 16.0),
                        ),
                        onPressed: () {},
                        shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(25.0)),
                      ),
                    ),
                  ),
                ), //text: product count
                Expanded(
                  child: RawMaterialButton(
                      onPressed: () {},
                      child: Icon(
                        Icons.add,
                        color: colorPrimary,
                        size: 16.0,
                      ),
                      shape: CircleBorder(),
                      elevation: 2.0,
                      fillColor: colorWhite,
                      padding: EdgeInsets.all(6.0),
                      splashColor: colorPrimary),
                ) //button: increase product count
              ],
            ),
          )
        ],
      ),
    ),
  ),
);
}

//get "top distributors" list
Widget _buildSliverTopDistributorsList() {
return SliverToBoxAdapter(
  child: Container(
    color: colorGrey,
    height: 100,
    child: ListView(
      padding: EdgeInsets.only(left: 16.0, right: 16.0),
      scrollDirection: Axis.horizontal,
      children: _listCategoryModel.map((itemCategory) {
        return _buildListTileTopDistributors();
      }).toList(),
    ),
  ),
);
}

Widget _buildListTileTopDistributors() {
var width = (getScreenWidth(context) / 4);
var height = (getScreenWidth(context) / 6);
return Padding(
  padding: const EdgeInsets.only(top: 8.0, bottom: 8.0),
  child: Container(
      alignment: Alignment.center,
      width: width,
      child: Material(
        color: Colors.transparent,
        child: InkWell(
          splashColor: colorPrimary,
          onTap: () {},
          child: Card(
            elevation: 8.0,
            color: colorPrimary,
            child: Container(
              width: width,
              height: height,
            ),
          ),
        ),
      )),
);
}

//get "clothing" two item
Widget _buildSliverClothing() {
return SliverToBoxAdapter(
  child: Container(
    padding: EdgeInsets.all(16.0),
    color: colorWhite,
    child: Column(
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.only(bottom: 8.0),
          child: Row(
            children: <Widget>[
              Expanded(child: Text('Clothing')),
              Text(
                'View all',
                style: TextStyle(color: colorPrimary),
              ),
            ],
          ),
        ), //text: "Popular Products" title
        Row(
          children: <Widget>[getItem(), getItem()],
        )
      ],
    ),
  ),
);
}

//get "clothing" two item
Widget _buildSliverElectronics() {
return SliverToBoxAdapter(
  child: Container(
    padding: EdgeInsets.all(16.0),
    color: colorWhite,
    child: Column(
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.only(bottom: 8.0),
          child: Row(
            children: <Widget>[
              Expanded(child: Text('Electronics')),
              Text(
                'View all',
                style: TextStyle(color: colorPrimary),
              ),
            ],
          ),
        ), //text: "Popular Products" title
        Row(
          children: <Widget>[getItem(), getItem()],
        )
      ],
    ),
  ),
);
}

//get "clothing" two item
Widget _buildSliverHealthAndBeauty() {
return SliverToBoxAdapter(
  child: Container(
    padding: EdgeInsets.all(16.0),
    color: colorWhite,
    child: Column(
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.only(bottom: 8.0),
          child: Row(
            children: <Widget>[
              Expanded(child: Text('HealthAndBeauty')),
              Text(
                'View all',
                style: TextStyle(color: colorPrimary),
              ),
            ],
          ),
        ), //text: "Popular Products" title
        Row(
          children: <Widget>[getItem(), getItem()],
        )
      ],
    ),
  ),
);
}


final List<CategoryModel> _listCategoryModel = [
CategoryModel(
    id: 0,
    name: 'Clothing',
    imageUrl: '',
    description: 'sampledescription'),
CategoryModel(
    id: 1,
    name: 'Electronics',
    imageUrl: '',
    description: 'sampledescription'),
CategoryModel(
    id: 2, name: 'Watches', imageUrl: '', description: 'sampledescription'),
CategoryModel(
    id: 3, name: 'Beauty', imageUrl: '', description: 'sampledescription'),
CategoryModel(
    id: 4, name: 'Shoes', imageUrl: '', description: 'sampledescription'),
CategoryModel(
    id: 5,
    name: 'Furnitures',
    imageUrl: '',
    description: 'sampledescription'),
CategoryModel(
    id: 6, name: 'Grocery', imageUrl: '', description: 'sampledescription'),
];
}

谢谢您的配合。

你找到解决方案了吗?目前我也遇到了同样的问题... - jennymo
有人找到了解决这个问题的方法吗?我也遇到了同样的问题。 - Benjamin Rodriguez
2个回答

5

首先将ScrollController添加到您的CustomScrollView中,然后对偏移位置进行动画处理,例如:

class _HomeScreenState extends State<HomeScreen> {
 ScrollController scrollController;
 
  @override
void initState() {
super.initState();
scrollController = new ScrollController();
}
 @override
Widget build(BuildContext context) {
return CustomScrollView(
 controller = scrollController,
  slivers: <Widget>[
  .... your wiidgets...
 ],
);
/* this method should be called on search bar textField tapped or when on whatever 
 event 
*/
void toTop(){
 scrollController.animateTo(0,
      duration: Duration(milliseconds: 500), curve: Curves.easeInOut);
}

希望这篇文章能够帮助其他人......


0

也许这不是最佳选择,但它可以解决你的一些问题。

import 'package:flutter/material.dart';

class TestFile extends StatefulWidget {
  const TestFile({Key? key}) : super(key: key);

  @override
  State<TestFile> createState() => _TestFileState();
}

class _TestFileState extends State<TestFile> {
  final ScrollController _scrollController = ScrollController();
  GlobalKey key = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          RenderBox box = key.currentContext?.findRenderObject() as RenderBox;
          Offset position =
              box.localToGlobal(Offset.zero); //this is global position
          double y = position.dy;

          _scrollController.animateTo(
            y,
            duration: const Duration(milliseconds: 500),
            curve: Curves.decelerate,
          );
        },
      ),
      body: CustomScrollView(
        controller: _scrollController,
        slivers: [
          SliverToBoxAdapter(
            child: Container(
              color: Colors.black,
              height: 700,
            ),
          ),
          SliverToBoxAdapter(
            child: Container(
              color: Colors.orange,
              height: 700,
            ),
          ),
          SliverToBoxAdapter(
            child: Container(
              color: Colors.yellow,
              height: 700,
            ),
          ),
          SliverToBoxAdapter(
            child: Container(
              key: key,
              color: Colors.white,
              height: 700,
            ),
          ),
          SliverToBoxAdapter(
            child: Container(
              color: Colors.red,
              height: 700,
            ),
          ),
        ],
      ),
    );
  }
}

这是一个完整的示例,您可以使用偏移量滚动并通过键获取特定小部件的偏移量。 我知道滚动后偏移值会改变,但您可以使用简单的状态管理器或存储来处理此问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接