flutter CustomscrollView滚动到顶部自动定位,当我点击一个文本字段在长条应用程序栏

3wabscal  于 2022-11-17  发布在  Flutter
关注(0)|答案(2)|浏览(255)

开发了一个顶部和底部都有搜索栏的页面,我们有水平列表视图和垂直列表视图...当我点击搜索栏文本字段时,我的下方视图自动滚动到顶部位置。
当我调试它的时候,构建方法再次被调用,如何解决这个问题?
这个问题也发生在这个示例https://github.com/lohanidamodar/flutter_ui_challenges中,它是我从中引用的。

src\pages\hotel\hhome.dart是网页

这是我的代码如果您觉得代码太长,请查看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'),
];
}

谢谢你,占用你的时间。

wqsoz72f

wqsoz72f1#

首先将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);
}

希望这对其他人有帮助。

lf5gs5x2

lf5gs5x22#

也许这不是最好的选择,但它可以涵盖您的一些问题。

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,
            ),
          ),
        ],
      ),
    );
  }
}

这是一个完整的例子,你可以滚动一个偏移量,并通过一个键获得特定小部件的偏移量。

相关问题