A Flutter Web Plugin to display Text Widget as Html for SEO purpose

Overview

SEO Renderer

Pub

A flutter plugin (under development) to render text widgets as html elements for SEO purpose.

Created specifically for issue https://github.com/flutter/flutter/issues/46789 It will automatic detect the crawler using regex and navigator userAgent and add the DivElement(only) to DOM.

All PR's are welcome :)

Getting Started

  • Add this to your pubspec.yaml

    dependencies:
      seo_renderer: ^0.1.0
    
    
  • Get the package from Pub:

    flutter packages get
    
  • Import it in your file

    import 'package:seo_renderer/seo_renderer.dart';
    

Usage

First we need to add a RouteObserver to automatically remove Html Elements when popped from the Navigation Stack. To do this simply add this line in MaterialApp

navigatorObservers: <RouteObserver<ModalRoute<void>>>[ routeObserver ],

ps : routeObserver is an object, which can be found in utils.dart file. For now there are 2 Widgets, TextRenderer & LinkRenderer

  • TextRenderer Just pass your Text/RichText Widget and an optional RenderController() which can be used to refresh the content(position) in case of Scrollable Content/ Changed Position .

    Example :

    TextRenderer(
           text: Text(
               'Lorem Ipsum is simply dummy text of the printing and typesetting industry.'),
         ),
    
  • LinkRenderer Need to pass child : Widget, anchorText : String, link : String & an optional RenderConroller()

    Example :

        LinkRenderer(
          anchorText: 'Try Flutter',
          link: 'https://www.flutter.dev',
          child: OutlinedButton(
            onPressed: () {
              launch('https://www.flutter.dev');
            },
            child: Text('Flutter.dev'),
          ),
        ),
    

RenderController have 2 methods

  • refresh() : Useful in case Widget changes position and you want it too.
  • clear() : Sometimes on Push operation Html Elements can't be removed. Please use this in your Push Operation.

ScreenShot & Example

Live example https://seo-renderer.netlify.app/

Select GoogleBot here's how as userAgent in Network Condition and refresh the page to see created Div Elements.

License

MIT License

Copyright (c) 2021 Sahdeep Singh

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Author & support

This project is created by Sahdeep Singh

If you appreciate my work you can connect and endorse me on LinkedIn to keep me motivated :)

Comments
  • Use isbot package to detect robot

    Use isbot package to detect robot

    Potentially it could be better to use something like this https://www.npmjs.com/package/isbot to detect the robot:

    <script>
      if (isbot(navigator.userAgent)) {
        window.flutterWebRenderer = "html";
      }
    </script>
    

    And pass simple boolean down to RobotDetector dart code, just that I'm not that familiar with NPM and how to use it within html.

    enhancement 
    opened by krokyze 4
  • Range Error (index) when using TextRenderer inside a List

    Range Error (index) when using TextRenderer inside a List

    Hello, I'm trying to fetch an list of names and it keeps getting error of index when using ListView or Column to map a List, here's the example page

    import 'package:flutter/material.dart';
    import 'package:flutter_modular/flutter_modular.dart';
    import 'package:flutter_web_poc/presentation/controllers/seo/seo_controller.dart';
    import 'package:seo_renderer/seo_renderer.dart';
    
    class SeoPage extends StatefulWidget {
      const SeoPage({Key? key}) : super(key: key);
    
      @override
      State<SeoPage> createState() => _SeoPageState();
    }
    
    class _SeoPageState extends State<SeoPage> {
      final controller = Modular.get<SeoController>();
    
      @override
      void initState() {
        super.initState();
        controller.fetchAllTodos();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const TextRenderer(
              child: Text('Seo Page'),
            ),
          ),
          body: AnimatedBuilder(
            animation: controller,
            builder: (context, widget) {
              return ListView.builder(
                itemCount: controller.todos.length,
                itemBuilder: (context, index) {
                  final todo = controller.todos[index];
                  return ListTile(
                    title: TextRenderer(
                      style: TextRendererStyle.header1,
                      child: Text(
                        todo.title,
                      ),
                    ),
                  );
                },
              );
            },
          ),
        );
      }
    }
    

    Here's my RobotDetector widget, it seems to me that nothing is wrong with it

    class MyApp extends StatelessWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        Modular.setObservers(
            [seoRouteObserver]); // replacement for navigatorObservers
        return RobotDetector(
          debug: true,
          child: MaterialApp.router(
            title: 'Flutter Web Poc',
            routeInformationParser: Modular.routeInformationParser,
            routerDelegate: Modular.routerDelegate,
            debugShowCheckedModeBanner: false,
          ),
        );
      }
    }
    

    Error Message:

    The following IndexError was thrown during a scheduler callback:
    RangeError (index): Index out of range: index must not be negative: -12
    
    When the exception was thrown, this was the stack
    dart-sdk[/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart]() 251:49  throw_
    dart-sdk[/lib/_internal/js_dev_runtime/private/js_array.dart]() 581:7 _get]
    lib[/_engine/engine/canvaskit/embedded_views.dart]() 647:52 [_updateOverlays] 
    packages[/flutter/src/rendering/binding.dart]() 501:18                            drawFrame
    packages[/flutter/src/widgets/binding.dart]() 883:13                              drawFrame
    packages[/flutter/src/rendering/binding.dart]() 363:5                             [_handlePersistentFrameCallback]
    packages[/flutter/src/scheduler/binding.dart]() 1144:15                           [_invokeFrameCallback]
    packages[/flutter/src/scheduler/binding.dart]() 1081:9                            handleDrawFrame
    packages[/flutter/src/scheduler/binding.dart]() 995:5                             [_handleDrawFrame]
    lib[/_engine/engine/platform_dispatcher.dart]() 1011:13                           invoke
    lib[/_engine/engine/platform_dispatcher.dart]() 159:5                             invokeOnDrawFrame
    lib[/_engine/engine/initialization.dart]() 128:45                                 <fn>
    ════════════════════════════════════════════════════════════════════════════════
    

    I'm using the Modular package for dependency injection by the way

    • Flutter version: 2.10.2
    • Seo_Renderer lib version: 0.0.5
    opened by felipesses 2
  • TextRenderer Widget throws

    TextRenderer Widget throws "Unexpected null value." at using inside a Row or Column

    I'm trying to use TextRenderer inside to any Rows and Columns and always throws me error. But, the showed error don't say me what the problem, because, it's throwed a default error. Any observations:

    • Flutter version: 2.10
    • Seo_Renderer lib version: 0.0.5

    Example code:

    Row(
      mainAxisAlignment: MainAxisAlignment.start,
      children: [
      InkWell(
        onTap: () {
          goToHomePage();
        },
        child: Container(
          width: 500,
          height: 200,
          child: TextRenderer(
            text: "Choose more products",
            style: SeoUtils.getTextRendererStyleByFontSize(14),
            child: Text(
              "Choose more products",
              style: TextStyle(
                fontSize: 14,
                color: const Color(0xFF3B3B3B),
                decoration: TextDecoration.underline,
                decorationColor: const Color(0xFF3B3B3B),
                fontWeight: FontWeight.bold,
              ),
            ),
          ),
        ),
      ),
      Container(child: Icon(Icons.add),),
      ],
      );
    

    Follow below stack errors:

    ════════ Exception caught by widgets library ═══════════════════════════════════ The following TypeErrorImpl was thrown building TextRenderer(dirty, dependencies: [_ModalScopeStatus], state: _TextRendererState#a019f): Unexpected null value.

    The relevant error-causing widget was TextRenderer When the exception was thrown, this was the stack C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/internal/js_dev_runtime/private/ddc_runtime/errors.dart 251:49 throw C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 528:63 nullCheck packages/seo_renderer/helpers/robot_detector_web.dart 20:68 detected packages/seo_renderer/renderers/text_renderer/text_renderer_web.dart 94:24 build packages/flutter/src/widgets/framework.dart 4870:27 build packages/flutter/src/widgets/framework.dart 4754:15 performRebuild packages/flutter/src/widgets/framework.dart 4928:11 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4919:11 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 6284:14 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 6284:14 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4928:11 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4919:11 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 6284:14 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 6284:14 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4928:11 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4919:11 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 6284:14 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4928:11 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4919:11 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4928:11 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4919:11 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4928:11 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4919:11 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 6422:36 inflateWidget packages/flutter/src/widgets/framework.dart 6433:32 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4928:11 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4919:11 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 6422:36 inflateWidget packages/flutter/src/widgets/framework.dart 6433:32 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 6284:14 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 6284:14 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 6284:14 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 6284:14 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 6284:14 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4928:11 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4919:11 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/framework.dart 4780:16 performRebuild packages/flutter/src/widgets/framework.dart 4477:5 rebuild packages/flutter/src/widgets/framework.dart 4735:5 [_firstBuild] packages/flutter/src/widgets/framework.dart 4729:5 mount packages/flutter/src/widgets/framework.dart 3790:13 inflateWidget packages/flutter/src/widgets/framework.dart 3540:18 updateChild packages/flutter/src/widgets/sliver.dart 1243:37 updateChild packages/flutter/src/widgets/sliver.dart 1228:20 packages/flutter/src/widgets/framework.dart 2600:19 buildScope packages/flutter/src/widgets/sliver.dart 1221:5 createChild packages/flutter/src/rendering/sliver_multi_box_adaptor.dart 349:23 packages/flutter/src/rendering/object.dart 1997:59 packages/flutter/src/rendering/object.dart 918:15 [_enableMutationsToDirtySubtrees] packages/flutter/src/rendering/object.dart 1997:7 invokeLayoutCallback packages/flutter/src/rendering/sliver_multi_box_adaptor.dart 338:5 [_createOrObtainChild] packages/flutter/src/rendering/sliver_multi_box_adaptor.dart 484:5 insertAndLayoutChild packages/flutter/src/rendering/sliver_list.dart 239:19 advance packages/flutter/src/rendering/sliver_list.dart 281:12 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/viewport.dart 510:12 layoutChildSequence packages/flutter/src/rendering/viewport.dart 1580:12 [_attemptLayout] packages/flutter/src/rendering/viewport.dart 1489:20 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/custom_paint.dart 545:11 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/custom_layout.dart 171:10 layoutChild packages/flutter/src/material/scaffold.dart 1005:7 performLayout packages/flutter/src/rendering/custom_layout.dart 240:7 [_callPerformLayout] packages/flutter/src/rendering/custom_layout.dart 403:14 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/proxy_box.dart 1376:11 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/proxy_box.dart 3430:13 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/widgets/overlay.dart 751:14 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/widgets/layout_builder.dart 321:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/custom_layout.dart 171:10 layoutChild packages/flutter/src/material/scaffold.dart 1005:7 performLayout packages/flutter/src/rendering/custom_layout.dart 240:7 [_callPerformLayout] packages/flutter/src/rendering/custom_layout.dart 403:14 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/proxy_box.dart 1376:11 performLayout packages/flutter/src/rendering/object.dart 1887:7 layout packages/flutter/src/rendering/proxy_box.dart 116:7 performLayout packages/flutter/src/rendering/object.dart 1731:7 [_layoutWithoutResize] packages/flutter/src/rendering/object.dart 887:17 flushLayout packages/flutter/src/rendering/binding.dart 497:19 drawFrame packages/flutter/src/widgets/binding.dart 883:13 drawFrame packages/flutter/src/rendering/binding.dart 363:5 [_handlePersistentFrameCallback] packages/flutter/src/scheduler/binding.dart 1144:15 [_invokeFrameCallback] packages/flutter/src/scheduler/binding.dart 1081:9 handleDrawFrame packages/flutter/src/scheduler/binding.dart 995:5 [_handleDrawFrame] C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/_engine/engine/platform_dispatcher.dart 1011:13 invoke C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/_engine/engine/platform_dispatcher.dart 159:5 invokeOnDrawFrame C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/_engine/engine/initialization.dart 128:45 ════════════════════════════════════════════════════════════════════════════════

    opened by weth767 2
  • RendererScrollListener widget missing

    RendererScrollListener widget missing

    Hi! First of all thank you for creating this amazing package and could the only solution available for flutter web SEO issue.

    I would like to ask on how to implement the RendererScrollListener using the package as it is not readily available upon importing the package.

    I did try to create my own by listening to the ScrollController and refreshing on ScrollEnd however this is not a very performant solution. Is it possible to give suggestion if in case the RendererScrollListener is not available? thank you.

    opened by rirjkl19 2
  • Implement ScrollAware within *Renderer widgets

    Implement ScrollAware within *Renderer widgets

    This pull request implements RendererScrollListener just so all renderer widgets can easily subscribe to scroll updates and no extra controllers need to be passed down the widget tree.

    opened by krokyze 2
  • Can not detect GoogleBot userAgent in Network Condition and refresh the page to see created Div Elements.

    Can not detect GoogleBot userAgent in Network Condition and refresh the page to see created Div Elements.

    I'm wrapping my MaterialApp with RobotDetector and Text widgets with TextRenderer. But when I Inspect Elements with GoogleBot userAgent, there is no extra html div's created by [seo_renderer] which google's bot gonna detect.

    Which is mentioned in your app .

    opened by esc-0-bar 1
  • Refactor all widgets to HtmlElementView

    Refactor all widgets to HtmlElementView

    This pull request:

    • Migrates all widgets to HtmlElementView so now scrolling, updating absolute positions, adding, removing html elements is automatically handled and there's no need of RenderScrollListener or RouteObserver.
    • Adds TextRendererStyle to fix #10
    • Moves _regExp.hasMatch to root RobotDetector so it isn't called every single time *Renderer widget builds
    • Adds debug option so robot mode can be easily enabled
    • Adds optional text for TextRenderer
    • Changes src to optional for ImageRenderer
    opened by krokyze 1
  • support specifying heading elements without importing dart:html

    support specifying heading elements without importing dart:html

    It would be nice if Header elements could be specified for TextRenderer without having to import dart:html. This would allow making sure certain things are rendered as Headers when needed without making the code dependent on web.

    One approach for doing this would be to replace the element parameter on TextRenderer with an Enum like this:

    enum
    enum HtmlTextType {
      paragraph,
      header1,
      header2,
      header3,
      header4,
      header5,
      header6,
    }
    

    Both the web text renderer and the vm text renderer could accept this parameter and handle it differently: by converting it into a Html Heading element on web and ignoring it on VM.

    type conversion
      HtmlElement typeToElement(HtmlTextType? type) {
        switch (type) {
          case HtmlTextType.header1:
            return HeadingElement.h1();
          case HtmlTextType.header2:
            return HeadingElement.h2();
          case HtmlTextType.header3:
            return HeadingElement.h3();
          case HtmlTextType.header4:
            return HeadingElement.h4();
          case HtmlTextType.header5:
            return HeadingElement.h5();
          case HtmlTextType.header6:
            return HeadingElement.h6();
          case HtmlTextType.paragraph:
          default:
            return ParagraphElement();
        }
      }
    
    opened by clragon 1
  • Why doesn't there exists not html tag but only canvas tag?

    Why doesn't there exists not html tag but only canvas tag?

    Hi iamSahdeep. I tried running your sample in your library, but couldn't find the difference in chrome developer's tool. I want to know if it is working for now. There is no div tag when I run this project.

    opened by changgyu-brandi 1
  • Feature/issues/5 add other elements than div

    Feature/issues/5 add other elements than div

    Hi @iamSahdeep,

    This is a MR about that ticket: https://github.com/iamSahdeep/seo_renderer/issues/5 Give your feedback or maybe do some ajustement.

    I want to know if it feels right to you.

    Thx

    opened by t1gu1 1
  • Add conditional imports to support including the widgets in VM builds.

    Add conditional imports to support including the widgets in VM builds.

    A quick example to demonstrate how to conditionally import different versions of the seo_renderer widgets that makes it possible to add the seo_renderer widgets also to VM builds. It only returns stubs that don't do anything useful on VM, and only adds the SEO renderer divs on the Web build.

    Using a shared abstract class could be nice improvement to ensure they always use the same interface.

    The changes might seem big, but are not. I just had to move the content in link_renderer.dart to link_renderer_web.dart and text_renderer.dart to text_renderer_web.dart and make them conditionally import the original file of the "web" version in web builds and the added "_vm" stub version for inclusion in none web builds.

    Also added runners for all platforms to the example, so I could try the build on other platforms too.

    Now it builds on them too and you can include the seo_renderer on desired widgets also in cross platform projects. It does not add much overhead on VM builds, other than a bit extra "noise" in the tree, but if you need it for your Web cross-platform build, it is well worth the added "noise".

    opened by rydmike 1
  • No longer need of ? operation for SchedulerBinding.instance

    No longer need of ? operation for SchedulerBinding.instance

    This is a very old problem which was not fixed in this package. Since flutter 3.0.0 just made this stable and the latest stable flutter version is 3.3.0 , this issue should be fixed a long time ago.

    opened by esc-0-bar 0
  • migrate flutter version to 3

    migrate flutter version to 3

    I created this pull request to prevent warnings in Flutter 3. Here is what I did. What do you think about this?

    • minimum flutter sdk version to 3.0.0
    • remove null check operator from SchedulerBinding
    • update pubs
    • add lint directive
    opened by cy-yusuke-abe 2
  • Another approach that I use in case your curious

    Another approach that I use in case your curious

    I used a different solution , which I used for a similar problem.

    The content of a flutter web app is not in the flutter web app. It’s coming from the database and static files on the server.

    This includes both the static text and images like you would have in your flutter code, and the dynamic text and images that comes from the server anyways.

    In the flutter web app it asks for its content from the server and also caches the static content , so it does not have to ask for it again. So it’s really just doing templating client side but not exactly.

    when a google ( or other search engine ) not requests a page , it gets a different template on the server and mixes it together with the content and returns it.

    This works very well and is a clean solution. But it requires more work I admit.

    The mobile flutter app is the same as the web flutter code. It also asks for the static content from the server and then caches it . And for dynamic data is also asks the server for the data.

    Often you need to do things like this anyway for apps that need to be in many languages anyway. Called i18n.

    If you have used the flutter translation tools you will see that it extracts all static text out of flutter code into an arb and json file. So you just need to put that file on the server. Then when the flutter code runs on the client , it pulls the json file up from the server and binds the text into the flutter code and it all renders.

    inages use the exact same trick. You just need to build a flutter AST parser that returns back the images in the same way the text translation does.

    The advantage is that your flutter binaries will be quite a bit smaller. Especially if you have lots of static text and images in your flutter code.

    i actually stopped using flutter, but not because of the way flutter web works with SEO. I just found flutter to be very immature compared to other systems in rust and golang which do exactly what flutter does.

    with golang and rust approaches you have a better language to do complex things . I know that flutter is great but I already knew golang and rust and so using gioui ( golang ) and yew ( rust ) is more efficient for me and other work colleagues.

    Anyways I hope the pattern I described above helps other developers using flutter and wishing to get high quality SEO.

    you can also output json-ld data to make the google search system even happier.

    opened by gedw99 0
  • How to integrate go_router with seo_renderer?

    How to integrate go_router with seo_renderer?

    Hello, I am using go_router for my project. Recently I wanted to integrate seo_renderer into my project.

    https://pub.dev/packages/go_router

    https://pub.dev/packages/seo_renderer

    However, the examples show that these 2 libraries are not compatible with each other.

    // seo_renderer:
    runApp(
      RobotDetector(
        debug: true, // you can set true to enable robot mode
        child: MaterialApp(
          home: MyApp(),
          navigatorObservers: [seoRouteObserver],
        ),
      ),
    );
    
    // go_router
    final _router = GoRouter(
      observers: [seoRouteObserver], // Not working.
      routes: [ ...]
    );
    // navigatorObservers is setted to null.
    MaterialApp.router(
       routeInformationParser: _router.routeInformationParser,
       routerDelegate: _router.routerDelegate,
      title: 'GoRouter Example',
    );
    

    This my my question in stackoverflow:

    https://stackoverflow.com/questions/72500928/how-to-integrate-go-router-with-seo-renderer

    Hope to get an example from you.

    opened by o7planning 2
  • Using seo_renderer with Navigator 2.0

    Using seo_renderer with Navigator 2.0

    Hello,

    I'm very interesting by your lib, but i cannot implement it if I use Navigator 2.0.

    In my main, i'm using MaterialApp.router(, and there is no observers or navigatorObserver key.

    Do you have a solution for that ?

    Many thanks

    opened by gabmagnan 4
Releases(v0.5.0)
  • v0.5.0(Feb 23, 2022)

    0.5.0

    • Using Widget RobotDetector to detect google bot etc. debug option to enable robot mode even if user agent don't have it.
    • routeObserver name changed to seoRouteObserver
    • TextRenderer now can have Text or RichText as child or simply text as String. if both supplied text property will be prioritised.
    • style property now have TextRendererStyle which is Enum, defaults to TextRendererStyle.paragraph
    • LinkRenderer have text property instead of anchorText and href instead of link to provide Link.
    • ImageRenderer can have src which is String type and child which can be any ImageProvider. src will have priority in both them.
    • RenderScrollListener is removed and scrolls will be automatically handled.
    • All thanks to @krokyze, see PR11
    Source code(tar.gz)
    Source code(zip)
  • v0.4.0(Feb 9, 2022)

    New ScrollAware widget RendererScrollListener to listen and update renderer widgets without RenderController. see PR #9 Note : This version removes the need of RenderController

    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(Oct 18, 2021)

  • v0.2.0(Sep 9, 2021)

  • v0.1.0(Jul 5, 2021)

  • v0.0.2(Jun 28, 2021)

    Include TextRenderer and LinkRenderer Element with will be same as Widget width Automatically removing the Element in case of Pop Action.

    Source code(tar.gz)
    Source code(zip)
Owner
Sahdeep Singh
Sahdeep Singh
A native textfield that can be used in place of Flutter's TextField widget.

Better Textfield A native textfield that can be used in place of Flutter's TextField widget. Demo demo.mp4 Here are some screenshots of the demo app:

Abhay Maurya 1 Sep 13, 2022
Flutter real-time magnifying glass lens widget with Barrel/Pincushion distortion

MagnifyingGlass Flutter plugin Flutter real-time magnifying glass lens widget with Barrel/Pincushion distortion. Works on Android, iOS and desktop. Do

Marco Bavagnoli 9 Apr 26, 2022
A Navigator 2.0 based Flutter widget that automatically splits the screen into two views based on available space

A Navigator 2.0 based Flutter widget that automatically splits the screen into two views based on available space

null 5 Sep 17, 2022
Display array is a board that sets 6 ST7735 display with a resolution of 80x160px in a linear array sharing the clock, data, rs, backlight pins together

The display array is a board that sets 6 ST7735 display with a resolution of 80x160px in a linear array sharing the clock, data, rs, backlight pins together, and leaving individual access to the cs lines of each display, This board allows you to display images with a resolution of 480x160px.

Josue Alejandro Gutierrez 65 Sep 15, 2022
A Flutter package that makes it easy to customize and work with your Flutter desktop app's system tray.

system_tray A Flutter package that that enables support for system tray menu for desktop flutter apps. on Windows, macOS and Linux. Features: - Modify

AnTler 122 Sep 17, 2022
Flutter-Clock-and-Reminder-App - a highly functional clock and reminder app developed on flutter framework.

clock_app A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if thi

Umar Baloch 6 Aug 4, 2022
Typewriter Effect with Rich Text + *Correct* Text Wrapping

Typewriter Effect with Rich Text + Correct Text Wrapping I've spent way too long getting this right. This is meant as a base class for a UMG dialogue

Sam Bloomberg 28 Sep 19, 2022
Text - A spicy text library for C++ that has the explicit goal of enabling the entire ecosystem to share in proper forward progress towards a bright Unicode future.

ztd.text Because if text works well in two of the most popular systems programming languages, the entire world over can start to benefit properly. Thi

Shepherd's Oasis 205 Aug 30, 2022
A plugin that can display player information overhead

A plugin that can display player information overhead Config File At plugins/HeadShow/config.json { "updateTick":60,

HuoHua 0 Jun 17, 2022
A cross-platform (Android/iOS/Windows/macOS) cronet plugin for Flutter via `dart:ffi`

cronet_flutter A cross-platform (Android/iOS/Windows/macOS) cronet plugin for Flutter via dart:ffi

null 21 Sep 2, 2022
A new JS script plugin for flutter

js_script Run JS script. Usage // Create a JS context. JsScript script = JsScript(); // Define a class. var classInfo = ClassInfo<TestClass>( newI

null 5 Mar 8, 2022
This plugin allows Flutter desktop apps to defines system tray.

tray_manager This plugin allows Flutter desktop apps to defines system tray. tray_manager Platform Support Quick Start Installation ⚠️ Linux requireme

LeanFlutter 103 Sep 20, 2022
A cross-platform flutter plugin for C/C++/ObjC crash report via Google Breakpad

quick_breakpad A cross-platform flutter plugin for C/C++/ObjC crash report via Google Breakpad Use breakpad for quick_breakpad_example $CLI_BREAKPAD i

Woodemi Co., Ltd 16 Jul 30, 2022
Flutter plugin serving utilities related to Windows taskbar. 💙

windows_taskbar Flutter plugin serving utilities related to Windows taskbar ?? Install dependencies: windows_taskbar: ^0.0.1 Demo Checkout the exam

Hitesh Kumar Saini 94 Sep 21, 2022
This plugin allows Flutter desktop apps to Auto launch on startup / login.

This plugin allows Flutter desktop apps to Auto launch on startup / login.

LeanFlutter 36 Sep 22, 2022
This plugin allows Flutter desktop apps to defines system/inapp wide hotkey (i.e. shortcut).

hotkey_manager This plugin allows Flutter desktop apps to defines system/inapp wide hotkey (i.e. shortcut). hotkey_manager Platform Support Quick Star

LeanFlutter 74 Sep 22, 2022
YouTube subscriber counter widget

YouTube subscriber counter My version of AlexGyver's project. My improvements Fix work with new youtube API Change subscribers displaying from 42000 t

null 1 Oct 26, 2021
imGuIZMO.quat is a ImGui widget: like a trackball it provides a way to rotate models, lights, or objects with mouse, and graphically visualize their position in space, also around any single axis (Shift/Ctrl/Alt/Super)

imGuIZMO.quat v3.0 imGuIZMO.quat is a ImGui widget: like a trackball it provides a way to rotate models, lights, or objects with mouse, and graphicall

Michele Morrone 265 Sep 16, 2022
A single-file, immediate-mode sequencer widget for C++17, Dear ImGui and EnTT

A single-file, immediate-mode sequencer widget for C++17, Dear ImGui and EnTT Table of Contents Overview Features Design Decisions Todo Open Questions

Alan Jefferson 187 Sep 10, 2022