需要限制屏幕宽度,但保持整个屏幕在Flutter Web应用中可滚动。

huangapple go评论72阅读模式
英文:

Need to restrict screen's width but keep the whole screen scrollable in flutter web app

问题

我正在制作一个Flutter Web应用程序,想要限制屏幕的宽度,但同时保持整个屏幕可滚动,就像Twitter网站一样。我使用主要的Scaffold来确定是否显示底部应用栏或导航栏。在Scaffold的主体部分,我放置了内部Scaffold,包裹在ConstrainedBox(maxWidth: 650)中,以限制内容的宽度。问题是滚动条附加到内部的Scaffold而不是屏幕。以下是最小的代码表示形式。

class MinimalHomeScreen extends StatelessWidget {
  const MinimalHomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final bool isMobile = constraints.maxWidth <= 600;

        return Scaffold(
          // 如果宽度较小,则显示底部栏
          bottomNavigationBar: isMobile
              ? BottomNavigationBar(
                  items: [
                    BottomNavigationBarItem(icon: Icon(Icons.home)),
                    BottomNavigationBarItem(icon: Icon(Icons.search)),
                  ],
                )
              : null,
          // 内容居中,最大宽度为650
          body: Center(
            child: ConstrainedBox(
              constraints: BoxConstraints(maxWidth: 650),
              child: Row(
                children: [
                  // 如果宽度不小,则显示导航栏
                  if (!isMobile)
                    NavigationRail(
                        destinations: [
                          NavigationRailDestination(icon: Icon(Icons.home), label: Text('Home')),
                          NavigationRailDestination(icon: Icon(Icons.search), label: Text('Search')),
                        ],
                        selectedIndex: 0,
                    ),

                  // 显示内容
                  Expanded(
                    child: Scaffold(
                      appBar: AppBar(title: Text('Title')),
                      body: ListView.separated(
                        itemCount: 100,
                        itemBuilder: (_, __) {
                          return Container(
                            height: 50,
                            width: double.infinity,
                            color: Colors.green,
                          );
                        },
                        separatorBuilder: (_, __) {
                          return Divider();
                        },
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    );
  }
}

我需要从屏幕的任何部分滚动我的内容,但我真的不知道如何做到这一点。我还需要使用内部的Scaffold来在其中添加逻辑,例如在AppBar上的返回按钮。我认为我必须使用NestedScrollView或CustomScrollView,但经过几个小时的尝试后,我没有找到解决方案。我还查看了"responsive framework"包,但它仍然只为内容的可滚动部分而不是整个屏幕提供了滚动。如果有人知道如何解决这个典型问题,请帮助一下!

英文:

I'm making a flutter web application and want to restrict screen's width but at the same time keep the whole screen scrollable like it's done in twitter website.
需要限制屏幕宽度,但保持整个屏幕在Flutter Web应用中可滚动。

I use primary Scaffold to determine whether to show bottomAppBar or navigation rail. Inside body of that Scaffold I put inner Scaffolds wrapped iniside ContrainedBox(maxWidth: 650) so I restrict the width of the content. The problem is that scroll bar is attached to inner Scaffolds but not to the screen. Below is the minimum representation code.

class MinimalHomeScreen extends StatelessWidget {
  const MinimalHomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final bool isMobile = constraints.maxWidth &lt;= 600;

        return Scaffold(
          // show bottom bar if width is small
          bottomNavigationBar: isMobile
              ? BottomNavigationBar(
                  items: [
                    BottomNavigationBarItem(icon: Icon(Icons.home)),
                    BottomNavigationBarItem(icon: Icon(Icons.search)),
                  ],
                )
              : null,
          // The content is centered and its maxWidth is 650
          body: Center(
            child: ConstrainedBox(
              constraints: BoxConstraints(maxWidth: 650),
              child: Row(
                children: [
                  // show navigation rail if width is not small
                  if (!isMobile)
                    NavigationRail(
                        destinations: [
                          NavigationRailDestination(icon: Icon(Icons.home), label: Text(&#39;Home&#39;)),
                          NavigationRailDestination(icon: Icon(Icons.search), label: Text(&#39;Search&#39;)),
                        ],
                        selectedIndex: 0,
                    ),

                  // show content
                  Expanded(
                    child: Scaffold(
                      appBar: AppBar(title: Text(&#39;Title&#39;)),
                      body: ListView.separated(
                        itemCount: 100,
                        itemBuilder: (_, __) {
                          return Container(
                            height: 50,
                            width: double.infinity,
                            color: Colors.green,
                          );
                        },
                        separatorBuilder: (_, __) {
                          return Divider();
                        },
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    );
  }
}

需要限制屏幕宽度,但保持整个屏幕在Flutter Web应用中可滚动。

I need to scroll my content from any part of screen but I don't really know how to do that. I also need to use inner scaffolds to have logic inside them like back button on AppBar.
需要限制屏幕宽度,但保持整个屏幕在Flutter Web应用中可滚动。

I think I have to use NestedScrollView or CustomScrollView but I didn't come up with solution after hours of attemps. I also looked into 'responsive framework' package but it also remains scrolling for only scrollable part of content but not the whole screen. If someone knows how to solve this typical issue, please, help!

答案1

得分: 1

您的需求在Flutter框架中需要更多的努力才能实现。我能想到的可能解决方案是使用Stack将滚动部分与Appbar/NavigationRail部分分离。

以下是我的解决方案:(我只处理!mobile布局)

BottomNavigationBar bottomNavigationBar() {
  return BottomNavigationBar(
    items: const [
      BottomNavigationBarItem(icon: Icon(Icons.home), label: ''),
      BottomNavigationBarItem(icon: Icon(Icons.search), label: ''),
    ],
  );
}

NavigationRail navigationRail() {
  return NavigationRail(
    destinations: const [
      NavigationRailDestination(icon: Icon(Icons.home), label: Text('主页')),
      NavigationRailDestination(
          icon: Icon(Icons.search), label: Text('搜索')),
    ],
    selectedIndex: 0,
  );
}

class MinimalHomeScreen extends StatelessWidget {
  const MinimalHomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        alignment: Alignment.center,
        children: [
          Positioned.fill(
            child: SingleChildScrollView(
              child: Center(
                child: ConstrainedBox(
                  constraints: const BoxConstraints(maxWidth: 650),
                  child: Padding(
                    padding: const EdgeInsets.only(left: 72),
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        const SizedBox(height: 50),
                        for (var i = 0; i &lt; 100; i++) ...[
                          Container(
                            height: 50,
                            width: double.infinity,
                            color: Colors.green,
                          ),
                          const Divider()
                        ]
                      ],
                    ),
                  ),
                ),
              ),
            ),
          ),
          Positioned(
            top: 0,
            bottom: 0,
            width: 650,
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                navigationRail(),
                Expanded(
                  child: SizedBox(
                    height: 56,
                    child: AppBar(
                      title: const Text('标题'),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

请注意,上述代码中的文本内容已经翻译成中文。如果您需要进一步的帮助或有其他问题,请随时提出。

英文:

Your requirement needs more effort in Flutter framework to achieve this. The possible solution I can think of is to separate the scroll part and the Appbar/NavigationRail part apart by using Stack.

Here is my workaround: (I only handle the !mobile Layout)

BottomNavigationBar bottomNavigationBar() {
  return BottomNavigationBar(
    items: const [
      BottomNavigationBarItem(icon: Icon(Icons.home), label: &#39;&#39;),
      BottomNavigationBarItem(icon: Icon(Icons.search), label: &#39;&#39;),
    ],
  );
}

NavigationRail navigationRail() {
  return NavigationRail(
    destinations: const [
      NavigationRailDestination(icon: Icon(Icons.home), label: Text(&#39;Home&#39;)),
      NavigationRailDestination(
          icon: Icon(Icons.search), label: Text(&#39;Search&#39;)),
    ],
    selectedIndex: 0,
  );
}

class MinimalHomeScreen extends StatelessWidget {
  const MinimalHomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        alignment: Alignment.center,
        children: [
          Positioned.fill(
            child: SingleChildScrollView(
              child: Center(
                child: ConstrainedBox(
                  constraints: const BoxConstraints(maxWidth: 650),
                  child: Padding(
                    padding: const EdgeInsets.only(left: 72),
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        const SizedBox(height: 50),
                        for (var i = 0; i &lt; 100; i++) ...[
                          Container(
                            height: 50,
                            width: double.infinity,
                            color: Colors.green,
                          ),
                          const Divider()
                        ]
                      ],
                    ),
                  ),
                ),
              ),
            ),
          ),
          Positioned(
            top: 0,
            bottom: 0,
            width: 650,
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                navigationRail(),
                Expanded(
                  child: SizedBox(
                    height: 56,
                    child: AppBar(
                      title: const Text(&#39;Title&#39;),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

答案2

得分: 0

以下是您要翻译的内容:

"在 web 中,像这样监听整个屏幕滚动是可能的:

document.onMouseWheel.listen((event) {
  event.deltaY; // 滚动偏移
});

因此,我们可以获取滚动偏移,将其放入某个流中,从包含 ListView/GridView 等的小部件中获取它,计算 ScrollController 内部的偏移,并使用 ScrollController 的 jumpTo() 方法。但问题在于从 onMouseWheel 得到的轨迹板偏移量很小。经过数小时尝试不同的方法,如 Listener 小部件和一些库,我决定停止撞墙,重新设计我的应用程序。希望有一天能轻松解决这样的任务。"

英文:

It's possible to listen the whole screen scrolling in web like this:

document.onMouseWheel.listen((event) {
  event.deltaY; // scroll offset
});

So we can get scroll offset, put it in some stream, get it from widget which contains ListView/GridView etc, calculate offset inside ScrollController and use jumpTo() method of ScrollController. But the problem is that the given trackpad offset from onMouseWheel is little. So after hours of trying different ways like Listener widget and some libraries I decided to stop slaming my head against the wall and redisign my application. I hope there'll come a day when such a task is solved easily

huangapple
  • 本文由 发表于 2023年5月25日 14:18:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/76329407.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定