通过鼠标滚轮在 HtmlElementView 上方滚动 => 不起作用 (Flutter Web)

Scrolling above HtmlElementView by mouse wheel => doesn't work (Flutter Web)

嗨 Flutter 粉丝

我正在为我的 flutter 网站开发自定义图像小部件,以使我的网站像本地网站一样

我的问题

我的自定义图像小部件可以使用了,您也可以使用它,但是我的滚动有问题

自定义图像小工具 (AdaptiveImage)

一个使用 HtmlElementView 显示图像的小部件,它使用语义和 SEO 渲染包

import 'package:flutter/material.dart';
import 'package:seo_renderer/seo_renderer.dart';
import '../utils/platform_detector.dart';
import 'package:universal_html/html.dart' as html;
import 'dart:ui' as ui;

// ignore: must_be_immutable
class AdaptiveImage extends StatefulWidget {
  final String srcImage;
  final String altImage;
  final double width;
  final double height;
  final String hint;
  final String label;
  final String value;
  final Widget? nativeImage;

  final String imageName;

  const AdaptiveImage({
    required key,
    required this.srcImage,
    required this.altImage,
    required this.width,
    required this.height,
    required this.imageName,
    this.nativeImage,
    this.label = '',
    this.hint = '',
    this.value = '',
  }) : super(key: key);

  @override
  State<StatefulWidget> createState() => _AdaptiveImageState();
}

class _AdaptiveImageState extends State<AdaptiveImage> {
  @override
  initState() {

    html.IFrameElement _element = html.IFrameElement()
      ..width = '${widget.width}px'
      ..height = '${widget.height}px'
      ..style.width = "${widget.width}px"
      ..style.height = "${widget.height}px"
      ..style.border = 'none'
      ..style.padding = '0px'
      ..style.margin = '0px'
      ..srcdoc = """
        <!DOCTYPE html>
        <html>
          <body scroll="no" style="overflow: hidden">
            <img src='${widget.srcImage}' alt='${widget.altImage}' width="${widget.width - 15}px" height="${widget.height - 15}px"/>
          </body>
        </html>
        """;

    // ignore: undefined_prefixed_name
    ui.platformViewRegistry
        .registerViewFactory(widget.imageName, (int viewId) => _element);

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return PlatformDetector().isWeb()
        ? SizedBox(
            width: widget.width,
            height: widget.height,
            child: ImageRenderer(
              src: widget.srcImage,
              alt: widget.altImage,
              child: Semantics(
                  onTap: () {},
                  readOnly: true,
                  image: true,
                  value: widget.value,
                  label: widget.label,
                  hint: widget.hint,
                  child: HtmlElementView(
                    key: widget.key,
                    viewType: widget.imageName,
                    onPlatformViewCreated: (value) => debugPrint('text_$value'),
                  )),
            ),
          )
        : Semantics(
            onTap: () {},
            readOnly: true,
            image: true,
            value: widget.value,
            label: widget.label,
            hint: widget.hint,
            child: widget.nativeImage);
  }
}

首页

我的用于显示包含自定义图像小部件的小部件的页面(AdaptiveImage)

请搜索如下文本:<<<<<<<<<<<-------------------- ----------这是我的问题

import 'dart:math';

import 'package:design_ui/utils/app_color.dart';
import 'package:design_ui/utils/resources_path.dart';
import 'package:design_ui/utils/router/routers.dart';
import 'package:design_ui/view_model/home_view_model.dart';
import 'package:design_ui/widgets/adaptive_image.dart';
import 'package:design_ui/widgets/adaptive_link.dart';
import 'package:design_ui/widgets/adaptive_text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_improved_scrolling/flutter_improved_scrolling.dart';
import 'package:seo_renderer/renderers/text_renderer/text_renderer_style.dart';
import 'package:seo_renderer/seo_renderer.dart';
import 'package:url_launcher/link.dart';

import '../utils/constants.dart';
import '../utils/localization/app_localizations.dart';
import '../utils/platform_detector.dart';
import '../widgets/clippers.dart';


class HomePage extends StatelessWidget {
  HomePage({Key? key}) : super(key: key) {
    viewModel = HomeViewModel(); //Injection
  }

  late HomeViewModel viewModel;

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: SizedBox(
          width: MediaQuery.of(context).size.width,
          height: MediaQuery.of(context).size.height,
          child:
              //Web for mobile (Android & IOS)
              PlatformDetector().isWeb() &&
                      (Theme.of(context).platform == TargetPlatform.iOS ||
                          Theme.of(context).platform == TargetPlatform.android)
                  ? _singleScrollChild(context)
                  :
                  //Web for Desktops (Windows-MacOS-Linux)
                  _desktopWebScroller(context),
        ),
      ),
    );
  }





 _desktopWebScroller(BuildContext context) {
    return ImprovedScrolling(
        scrollController: viewModel.scrollController,
        mmbScrollConfig: const MMBScrollConfig(
          customScrollCursor: DefaultCustomScrollCursor(),
        ),
        keyboardScrollConfig: KeyboardScrollConfig(
          homeScrollDurationBuilder: (currentScrollOffset, minScrollOffset) {
            return const Duration(milliseconds: 100);
          },
          endScrollDurationBuilder: (currentScrollOffset, maxScrollOffset) {
            return const Duration(milliseconds: 2000);
          },
        ),
        customMouseWheelScrollConfig: const CustomMouseWheelScrollConfig(
          scrollAmountMultiplier: 2.0,
        ),
        onScroll: (scrollOffset) {
          //print(Scroll offset: $scrollOffset',),
        },
        onMMBScrollStateChanged: (scrolling) {
          //print('Is scrolling: $scrolling',)
        },
        onMMBScrollCursorPositionUpdate: (localCursorOffset, scrollActivity) {
          //print('Cursor position: $localCursorOffset\n''Scroll activity: $scrollActivity',)
        },
        enableMMBScrolling: true,
        enableKeyboardScrolling: true,
        enableCustomMouseWheelScrolling: true,
        child: ScrollConfiguration(
          behavior: const CustomScrollBehaviour(),
          child: _singleScrollChild(context),
        ));
  }





_singleScrollChild(BuildContext context) {
    return SingleChildScrollView(
      controller: viewModel.scrollController,
      physics:
          //Web to Android or IOS OS
          PlatformDetector().isWeb() &&
                  (Theme.of(context).platform == TargetPlatform.windows ||
                      Theme.of(context).platform == TargetPlatform.linux ||
                      Theme.of(context).platform == TargetPlatform.macOS ||
                      Theme.of(context).platform == TargetPlatform.fuchsia)
              ? const NeverScrollableScrollPhysics()
              :
              //Web to Windows or MacOs or Linux
              const BouncingScrollPhysics(),
      scrollDirection: Axis.vertical,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisSize: MainAxisSize.min,
        children: [
          //////////////////////// * Header * ////////////////////////
          HomeHeader(viewModel),

          //////////////////////// * Modern Products * ////////////////////////
          HomeModernProducts(viewModel),

        ],
      ),
    );
  }







/* HomeHeader Class */

// ignore: must_be_immutable
class HomeHeader extends StatelessWidget {
  HomeHeader(this.viewModel, {Key? key}) : super(key: key);

  HomeViewModel viewModel;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
        width: MediaQuery.of(context).size.width,
        height: MediaQuery.of(context).size.height,
        child: ClipPath(
          clipper: HomeHeaderClipper(),
          child: Stack(
            children: [
              //////////////////////// * Background Image * ////////////////////////
              ImageRenderer(
                alt: 'Products header image',
                src: 'assets${ResourcesPath.headerImage}',
                child: Image.asset(
                  ResourcesPath.headerImage,
                  fit: BoxFit.cover,
                  width: MediaQuery.of(context).size.width,
                  height: MediaQuery.of(context).size.height,
                ),
              ),

              // AdaptiveImage(
              //   width: MediaQuery.of(context).size.width,
              //   height: MediaQuery.of(context).size.height,
              //   altImage: 'products header image',
              //   srcImage: ResourcesPath.headerImage,
              //   hint: 'products header image',
              //   label: 'products header image',
              //   value: 'products header image',
              // ),

              //////////////////////// * Shadow Black * ////////////////////////
              Container(
                width: MediaQuery.of(context).size.width,
                height: MediaQuery.of(context).size.height,
                color: Colors.black.withOpacity(0.8),
              ),

              //////////////////////// * Titles * ////////////////////////
              Center(
                  child: Column(
                mainAxisSize: MainAxisSize.min,
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  //////////////////////// * Title * ////////////////////////
                  AdaptiveText(
                    text: AppLocalizations.of(context)!
                        .translate('home_header_title'),
                    headerText: 'Products',
                    headerType: TextRendererStyle.header1,
                    style: Theme.of(context).textTheme.headline1!.copyWith(
                        color: Colors.white,
                        shadows: const [
                          Shadow(color: Colors.blue, blurRadius: 12)
                        ]),
                    textAlign: TextAlign.center,
                    hint: 'Products',
                    label: 'Products',
                    value: 'Products',
                  ),
                  const SizedBox(
                    height: 20,
                  ),

                  //////////////////////// * Subtitle * ////////////////////////
                  Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: AdaptiveText(
                      text: AppLocalizations.of(context)!
                          .translate('home_header_subtitle'),
                      headerText:
                          "Explore the latest modern products in the world",
                      headerType: TextRendererStyle.header2,
                      style: Theme.of(context).textTheme.headline2!.copyWith(
                          color: Colors.white, fontWeight: FontWeight.w200),
                      textAlign: TextAlign.center,
                      hint: "Explore the latest modern products in the world",
                      label: "Explore the latest modern products in the world",
                      value: "Explore the latest modern products in the world",
                    ),
                  ),
                  const SizedBox(
                    height: 35,
                  ),

                  //////////////////////// * Button Login * ////////////////////////
                  AdaptiveLink(
                    link: Routers.loginName,
                    linkText: 'Login Page',
                    target: LinkTarget.self,
                    hint: 'Login Page',
                    label: 'Login Page',
                    value: 'Login Page',
                    widget: ElevatedButton(
                      style: ElevatedButton.styleFrom(
                        padding: const EdgeInsets.all(25),
                        primary: Theme.of(context).primaryColor,
                        shadowColor: Theme.of(context).primaryColor,
                        shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(10)),
                        elevation: 16,
                      ),
                      child: Text(
                        AppLocalizations.of(context)!
                            .translate('home_header_button_login'),
                        style: Theme.of(context)
                            .textTheme
                            .headline6!
                            .copyWith(color: Colors.white),
                        textAlign: TextAlign.center,
                      ),
                      onPressed: () {
                        viewModel.goLogin(context);
                      },
                    ),
                  )
                ],
              ))
            ],
          ),
        ));
  }
}








/*  ModernProducts Class */
// ignore: must_be_immutable
class HomeModernProducts extends StatelessWidget {
  HomeModernProducts(this.viewModel, {Key? key}) : super(key: key);

  HomeViewModel viewModel;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 42, vertical: 30),
      child: Row(
        mainAxisSize: MainAxisSize.max,
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          //////////////////////// * Text * ////////////////////////
          Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              AdaptiveText(
                text: AppLocalizations.of(context)!
                    .translate('home_modern_products_title'),
                headerText: "Modern Products",
                headerType: TextRendererStyle.header3,
                style: Theme.of(context)
                    .textTheme
                    .headline3!
                    .copyWith(fontWeight: FontWeight.w400),
                textAlign: TextAlign.start,
                hint: "Modern Products",
                label: "Modern Products",
                value: "Modern Products",
              ),
              const SizedBox(
                height: 12,
              ),
              SizedBox(
                width: MediaQuery.of(context).size.width - 800,
                child: AdaptiveText(
                  text: AppLocalizations.of(context)!
                      .translate('home_modern_products_subtitle'),
                  headerText:
                      "We offer a lot of products which every one want has it, we care about your ideas and opinion and release latest version of our products",
                  headerType: TextRendererStyle.header6,
                  style: Theme.of(context)
                      .textTheme
                      .headline6!
                      .copyWith(fontWeight: FontWeight.w200),
                  textAlign: TextAlign.start,
                  maxLines: 4,
                  hint:
                      "We offer a lot of products which every one want has it, we care about your ideas and opinion and release latest version of our products",
                  label:
                      "We offer a lot of products which every one want has it, we care about your ideas and opinion and release latest version of our products",
                  value:
                      "We offer a lot of products which every one want has it, we care about your ideas and opinion and release latest version of our products",
                ),
              ),
            ],
          ),

          //////////////////////// * Image * //////////////////////// 
          AdaptiveImage(            <<<<<<<<<<<---------------------------------HERE MY PROBLEM
            key: UniqueKey(),
            width: 660,
            height: 660,
            altImage: 'modern products image',
            srcImage: ResourcesPath.modernProductsImage,
            imageName: 'image${Random().nextInt(100000)}',
            hint: 'modern products image',
            label: 'modern products image',
            value: 'modern products image',
          ),
        ],
      ),
    );
  }
}

我想要的

请大家解决鼠标悬停在 AdaptiveImage 上时滚动的问题 (HtmlViewElement)

我做了什么和失败了

1- 用 SingleChildScrollView 包装我的 AdaptiveImage 并为其使用相同的控制器,代码:

SingleChildScrollView(
            controller: viewModel.scrollController,    //Using same scroller
            child: AdaptiveImage(
              key: UniqueKey(),
              width: 660,
              height: 660,
              altImage: 'modern products image',
              srcImage: ResourcesPath.modernProductsImage,
              imageName: 'image${Random().nextInt(100000)}',
              hint: 'modern products image',
              label: 'modern products image',
              value: 'modern products image',
            ),
          ),

结果:

Doesn't work

2- 用 SingleChildScrollView 包装我的 AdaptiveImage 并且不使用控制器,代码:

SingleChildScrollView(
            child: AdaptiveImage(
              key: UniqueKey(),
              width: 660,
              height: 660,
              altImage: 'modern products image',
              srcImage: ResourcesPath.modernProductsImage,
              imageName: 'image${Random().nextInt(100000)}',
              hint: 'modern products image',
              label: 'modern products image',
              value: 'modern products image',
            ),
          ),

结果:

Doesn't work

屏幕

您可以查看一些图片以获得更多解释

照片1

开始正常滚动

照片2

文本上方的左侧可以很好地滚动,但右侧不能在图像 (AdaptiveImage) 上方滚动,因为 HtmlViewElement,我想要一种在其上方滚动的方法

终于

非常感谢您耐心阅读所有这些内容并喜欢您对我的支持

是的,伙计们,我找到了

我正在研究我的问题的解决方案,我重写了 AdaptiveImage class,它 works 很好。

import 'package:flutter/material.dart';
import 'package:seo_renderer/seo_renderer.dart';
import '../utils/platform_detector.dart';
import 'package:universal_html/html.dart' as html;
import 'dart:ui' as ui;

// ignore: must_be_immutable
class AdaptiveImage extends StatefulWidget {
  final String srcImage;
  final String altImage;
  final double width;
  final double height;
  final String hint;
  final String label;
  final String value;
  final Widget? nativeImage;

  final String imageName;

  const AdaptiveImage({
    required key,
    required this.srcImage,
    required this.altImage,
    required this.width,
    required this.height,
    required this.imageName,
    this.nativeImage,
    this.label = '',
    this.hint = '',
    this.value = '',
  }) : super(key: key);

  @override
  State<StatefulWidget> createState() => _AdaptiveImageState();
}

class _AdaptiveImageState extends State<AdaptiveImage> {
  @override
  initState() {

    html.ImageElement _element = html.ImageElement(src: widget.srcImage)
      ..style.width = "${widget.width}px"
      ..style.height = "${widget.height}px"
      ..alt = widget.altImage
      ..style.padding = '0px'
      ..style.margin = '0px';

    // ignore: undefined_prefixed_name
    ui.platformViewRegistry
        .registerViewFactory(widget.imageName, (int viewId) => _element);

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return PlatformDetector().isWeb()
        ? SizedBox(
            width: widget.width,
            height: widget.height,
            child: ImageRenderer(
              src: widget.srcImage,
              alt: widget.altImage,
              child: Semantics(
                  onTap: () {},
                  readOnly: true,
                  image: true,
                  value: widget.value,
                  label: widget.label,
                  hint: widget.hint,
                  child: IgnorePointer(
                    child: HtmlElementView(
                      key: widget.key,
                      viewType: widget.imageName,
                    ),
                  )),
            ),
          )
        : Semantics(
            onTap: () {},
            readOnly: true,
            image: true,
            value: widget.value,
            label: widget.label,
            hint: widget.hint,
            child: widget.nativeImage);
  }
}