简体   繁体   中英

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

Hi Flutter Fans

I am working on custom image widget to my flutter website for making my website like native websites

My Problem

my custom image widget is ready for usage and you can use it too , but my problem with scrolling

  • when hovering by mouse in any place of my website (expect custom image widget) then scroll by mouse wheel => it is scrolling fine
  • when hovering on custom image widget and scroll by mouse wheel => can't scrolling

Custom Image Widget (AdaptiveImage)

A widget for display images using HtmlElementView , it use Semantics and SEO Render package

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

Home Page

My page for showing widgets that include custom image widget (AdaptiveImage)

Please search about text like : <<<<<<<<<<<---------------------------------HERE MY PROBLEM

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

what i want

Please guys need a solution for scrolling when hovering by mouse on AdaptiveImage ( HtmlViewElement )

what i did and failed

1- wrapped my AdaptiveImage by SingleChildScrollView and use same controller for it, code :

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',
            ),
          ),

Result :

Doesn't work

2- wrapped my AdaptiveImage by SingleChildScrollView and without using a controller , code :

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',
            ),
          ),

Result :

Doesn't work

Screens

You can check some images for more explaination

Photo1

start scrolling normally

在此处输入图像描述

Photo2

left side above texts can scroll fine , but right side can't scroll above image (AdaptiveImage) because HtmlViewElement , i want a way for scrolling above it

在此处输入图像描述

Finaly

I am very appreciate your patience to read all of that and like your support for me

Yes Guys i found it

i was research about a solution for my problem , i rewrite AdaptiveImage class and it works fine .

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM