You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@beam.apache.org by pa...@apache.org on 2022/08/10 23:10:21 UTC

[beam] branch master updated: [Playground] Share any code feature frontend (#22477)

This is an automated email from the ASF dual-hosted git repository.

pabloem pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git


The following commit(s) were added to refs/heads/master by this push:
     new 38cc8bc7cfc [Playground] Share any code feature frontend (#22477)
38cc8bc7cfc is described below

commit 38cc8bc7cfcb393ae959eb774cb7d8da6ea6a2b8
Author: alexeyinkin <al...@akvelon.com>
AuthorDate: Thu Aug 11 03:10:14 2022 +0400

    [Playground] Share any code feature frontend (#22477)
    
    * [Playground] Share any code feature (#21978)
    
    * Create separate loaders for different sources of loadable code (#21978)
    
    * Fix dark theme (#21978)
    
    Co-authored-by: Aleksandr Zhuravlev <al...@akvelon.com>
---
 playground/frontend/analysis_options.yaml          |   1 -
 .../dropdown_button/dropdown_button.dart           |  53 +++---
 .../horizontal_divider.dart}                       |  27 +--
 .../lib/components/split_view/split_view.dart      |   2 +-
 .../toggle_theme_button/toggle_theme_button.dart   |  10 +-
 .../toggle_theme_icon_button.dart                  |   4 +-
 playground/frontend/lib/config/theme.dart          | 110 +++++++++++-
 playground/frontend/lib/constants/params.dart      |   8 +-
 playground/frontend/lib/constants/sizes.dart       |   4 +
 playground/frontend/lib/l10n/app_en.arb            |  22 ++-
 .../modules/editor/components/editor_themes.dart   |   5 +-
 .../components/share_dropdown/link_text_field.dart | 103 +++++++++++
 .../components/share_dropdown/share_button.dart    |  58 ++++++
 .../share_dropdown/share_dropdown_body.dart}       |  54 +++---
 .../components/share_dropdown/share_tab_body.dart} |  32 ++--
 .../share_tabs/example_share_tabs.dart             |  67 +++++++
 .../share_dropdown/share_tabs/share_tabs.dart}     |  50 +++---
 .../share_tabs/snippet_save_and_share_tabs.dart    |  56 ++++++
 .../share_dropdown/share_tabs_headers.dart}        |  35 ++--
 .../example_list/expansion_panel_item.dart         |  14 +-
 .../lib/modules/examples/example_selector.dart     |  65 ++++---
 ...atalog_default_example_loading_descriptor.dart} |  25 ++-
 .../empty_example_loading_descriptor.dart}         |  25 ++-
 .../example_loading_descriptor.dart}               |  15 +-
 .../examples_loading_descriptor.dart}              |  26 ++-
 .../examples_loading_descriptor_factory.dart       |  79 +++++++++
 .../standard_example_loading_descriptor.dart}      |  27 ++-
 .../user_shared_example_loading_descriptor.dart}   |  28 ++-
 .../lib/modules/examples/models/example_model.dart |   4 +
 ...elector_size_model.dart => example_origin.dart} |  27 ++-
 ...tor_size_model.dart => example_token_type.dart} |  21 ++-
 .../example_client/example_client.dart             |  28 ++-
 .../example_client/grpc_example_client.dart        | 110 +++++++++++-
 .../examples/repositories/example_repository.dart  |  21 ++-
 .../models/get_snippet_request.dart}               |  10 +-
 .../models/get_snippet_response.dart}              |  17 +-
 .../models/save_snippet_request.dart}              |  17 +-
 .../models/save_snippet_response.dart}             |  10 +-
 .../models/shared_file_model.dart}                 |  14 +-
 .../lib/modules/output/components/output.dart      |  20 ++-
 .../output_header/output_placements.dart           |  41 +++--
 .../{output_header.dart => tab_header.dart}        |  25 +--
 .../lib/modules/sdk/components/sdk_selector.dart   |   7 +-
 .../frontend/lib/modules/sdk/models/sdk.dart       |  27 +++
 .../components/editor_textarea_wrapper.dart        |   9 +-
 .../feedback/feedback_dropdown_content.dart        |  10 +-
 .../components/playground_page_body.dart           |   4 +-
 .../components/playground_page_providers.dart      | 128 +------------
 .../lib/pages/playground/playground_page.dart      |  13 +-
 .../catalog_default_example_loader.dart            |  48 +++++
 .../example_loaders/empty_example_loader.dart}     |  50 ++----
 .../states/example_loaders/example_loader.dart}    |  12 +-
 .../states/example_loaders/examples_loader.dart    |  87 +++++++++
 .../example_loaders/standard_example_loader.dart   |  74 ++++++++
 .../user_shared_example_loader.dart}               |  21 ++-
 .../playground/states/example_selector_state.dart  |  17 +-
 .../pages/playground/states/examples_state.dart    |  92 ++++++++--
 .../pages/playground/states/playground_state.dart  |  33 +++-
 playground/frontend/lib/playground_app.dart        |  65 ++++---
 playground/frontend/lib/utils/dropdown_utils.dart  |  48 +++++
 .../frontend/lib/utils/share_code_utils.dart       | 111 ++++++++++++
 .../example_repository_test.mocks.dart             |  80 ++++++---
 .../states/example_selector_state_test.dart        |  20 +--
 .../states/example_selector_state_test.mocks.dart  |  89 ++--------
 .../playground/states/examples_state_test.dart     |  31 +---
 .../playground/states/mocks/example_mock.dart      |  22 ++-
 .../states/mocks/example_repository_mock.dart      |  51 ++++++
 .../example_repository_mock.mocks.dart}            |  76 +++++---
 .../playground/states/mocks/request_mock.dart      |   2 +-
 .../playground/states/playground_state_test.dart   |  25 ++-
 .../states/playground_state_test.mocks.dart        | 197 +++++++++++++++++++++
 71 files changed, 2079 insertions(+), 740 deletions(-)

diff --git a/playground/frontend/analysis_options.yaml b/playground/frontend/analysis_options.yaml
index fd91906eae8..340900ee193 100644
--- a/playground/frontend/analysis_options.yaml
+++ b/playground/frontend/analysis_options.yaml
@@ -100,7 +100,6 @@ linter:
     use_if_null_to_convert_nulls_to_bools: true
     use_is_even_rather_than_modulo: true
     use_raw_strings: true
-    use_setters_to_change_properties: true
     use_to_and_as_if_applicable: true
 
     #Turn off those on by default:
diff --git a/playground/frontend/lib/components/dropdown_button/dropdown_button.dart b/playground/frontend/lib/components/dropdown_button/dropdown_button.dart
index a554356c635..fa89fce72ff 100644
--- a/playground/frontend/lib/components/dropdown_button/dropdown_button.dart
+++ b/playground/frontend/lib/components/dropdown_button/dropdown_button.dart
@@ -19,26 +19,40 @@
 import 'package:flutter/material.dart';
 import 'package:playground/config/theme.dart';
 import 'package:playground/constants/sizes.dart';
-import 'package:playground/modules/examples/models/selector_size_model.dart';
+import 'package:playground/utils/dropdown_utils.dart';
 
 const int kAnimationDurationInMilliseconds = 80;
 const Offset kAnimationBeginOffset = Offset(0.0, -0.02);
 const Offset kAnimationEndOffset = Offset(0.0, 0.0);
-const double kAdditionalDyAlignment = 50.0;
+
+/// How to align the button and its dropdown.
+enum DropdownAlignment {
+  /// Align the left edges of the button and its dropdown.
+  left,
+
+  /// Align the right edges of the button and its dropdown.
+  right,
+}
 
 class AppDropdownButton extends StatefulWidget {
   final Widget buttonText;
   final Widget Function(void Function()) createDropdown;
   final double height;
   final double width;
+  final Widget? leading;
+  final bool showArrow;
+  final DropdownAlignment dropdownAlign;
 
   const AppDropdownButton({
-    Key? key,
+    super.key,
     required this.buttonText,
     required this.createDropdown,
     required this.height,
     required this.width,
-  }) : super(key: key);
+    this.leading,
+    this.showArrow = true,
+    this.dropdownAlign = DropdownAlignment.left,
+  });
 
   @override
   State<AppDropdownButton> createState() => _AppDropdownButtonState();
@@ -76,7 +90,7 @@ class _AppDropdownButtonState extends State<AppDropdownButton>
     return Container(
       height: kContainerHeight,
       decoration: BoxDecoration(
-        color: ThemeColors.of(context).greyColor,
+        color: ThemeColors.of(context).dropdownButton,
         borderRadius: BorderRadius.circular(kSmBorderRadius),
       ),
       child: TextButton(
@@ -88,8 +102,13 @@ class _AppDropdownButtonState extends State<AppDropdownButton>
             alignment: WrapAlignment.center,
             crossAxisAlignment: WrapCrossAlignment.center,
             children: [
+              if (widget.leading != null)
+                Padding(
+                  padding: const EdgeInsets.only(right: kMdSpacing),
+                  child: widget.leading,
+                ),
               widget.buttonText,
-              const Icon(Icons.keyboard_arrow_down),
+              if (widget.showArrow) const Icon(Icons.keyboard_arrow_down),
             ],
           ),
         ),
@@ -98,7 +117,11 @@ class _AppDropdownButtonState extends State<AppDropdownButton>
   }
 
   OverlayEntry createDropdown() {
-    SelectorPositionModel posModel = findSelectorPositionData();
+    final dropdownOffset = findDropdownOffset(
+      alignment: widget.dropdownAlign,
+      key: selectorKey,
+      widgetWidth: widget.width,
+    );
 
     return OverlayEntry(
       builder: (context) {
@@ -115,8 +138,8 @@ class _AppDropdownButtonState extends State<AppDropdownButton>
               ),
             ),
             Positioned(
-              left: posModel.xAlignment,
-              top: posModel.yAlignment + kAdditionalDyAlignment,
+              left: dropdownOffset.dx,
+              top: dropdownOffset.dy,
               child: SlideTransition(
                 position: offsetAnimation,
                 child: Material(
@@ -126,7 +149,7 @@ class _AppDropdownButtonState extends State<AppDropdownButton>
                     height: widget.height,
                     width: widget.width,
                     decoration: BoxDecoration(
-                      color: Theme.of(context).backgroundColor,
+                      color: ThemeColors.of(context).background,
                       borderRadius: BorderRadius.circular(kMdBorderRadius),
                     ),
                     child: widget.createDropdown(_close),
@@ -140,16 +163,6 @@ class _AppDropdownButtonState extends State<AppDropdownButton>
     );
   }
 
-  SelectorPositionModel findSelectorPositionData() {
-    RenderBox? rBox =
-        selectorKey.currentContext?.findRenderObject() as RenderBox;
-    SelectorPositionModel positionModel = SelectorPositionModel(
-      xAlignment: rBox.localToGlobal(Offset.zero).dx,
-      yAlignment: rBox.localToGlobal(Offset.zero).dy,
-    );
-    return positionModel;
-  }
-
   void _close() {
     animationController.reverse();
     dropdown?.remove();
diff --git a/playground/frontend/lib/components/toggle_theme_button/toggle_theme_icon_button.dart b/playground/frontend/lib/components/horizonta_divider/horizontal_divider.dart
similarity index 65%
copy from playground/frontend/lib/components/toggle_theme_button/toggle_theme_icon_button.dart
copy to playground/frontend/lib/components/horizonta_divider/horizontal_divider.dart
index a505e4febd6..f935eade102 100644
--- a/playground/frontend/lib/components/toggle_theme_button/toggle_theme_icon_button.dart
+++ b/playground/frontend/lib/components/horizonta_divider/horizontal_divider.dart
@@ -17,24 +17,25 @@
  */
 
 import 'package:flutter/material.dart';
-import 'package:flutter_svg/flutter_svg.dart';
 import 'package:playground/config/theme.dart';
-import 'package:playground/constants/assets.dart';
 import 'package:playground/constants/sizes.dart';
-import 'package:provider/provider.dart';
 
-class ToggleThemeIconButton extends StatelessWidget {
-  const ToggleThemeIconButton({Key? key}) : super(key: key);
+/// Replaces the Flutter's Divider which is buggy with HTML renderer,
+/// see https://github.com/flutter/flutter/issues/46339
+class HorizontalDivider extends StatelessWidget {
+  final double? indent;
+
+  const HorizontalDivider({
+    super.key,
+    this.indent,
+  });
 
   @override
   Widget build(BuildContext context) {
-    return Consumer<ThemeProvider>(builder: (context, theme, child) {
-      return IconButton(
-        iconSize: kIconSizeLg,
-        splashRadius: kIconButtonSplashRadius,
-        icon: SvgPicture.asset(kThemeIconAsset),
-        onPressed: theme.toggleTheme,
-      );
-    });
+    return Container(
+      height: kDividerHeight,
+      margin: EdgeInsets.fromLTRB(indent ?? 0, 0, indent ?? 0, 0),
+      color: ThemeColors.of(context).divider,
+    );
   }
 }
diff --git a/playground/frontend/lib/components/split_view/split_view.dart b/playground/frontend/lib/components/split_view/split_view.dart
index 922aea4e94c..06403d6759a 100644
--- a/playground/frontend/lib/components/split_view/split_view.dart
+++ b/playground/frontend/lib/components/split_view/split_view.dart
@@ -128,7 +128,7 @@ class _SplitViewState extends State<SplitView> {
         child: Container(
             width: _isHorizontal ? widget.dividerSize : double.infinity,
             height: _isVertical ? widget.dividerSize : double.infinity,
-            color: ThemeColors.of(context).greyColor,
+            color: ThemeColors.of(context).divider,
             child: Center(
               child: SvgPicture.asset(_isHorizontal
                   ? kDragHorizontalIconAsset
diff --git a/playground/frontend/lib/components/toggle_theme_button/toggle_theme_button.dart b/playground/frontend/lib/components/toggle_theme_button/toggle_theme_button.dart
index 1d55e600c0c..730b5b240b9 100644
--- a/playground/frontend/lib/components/toggle_theme_button/toggle_theme_button.dart
+++ b/playground/frontend/lib/components/toggle_theme_button/toggle_theme_button.dart
@@ -30,10 +30,10 @@ class ToggleThemeButton extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    AppLocalizations appLocale = AppLocalizations.of(context)!;
+    final appLocale = AppLocalizations.of(context)!;
 
-    return Consumer<ThemeProvider>(builder: (context, theme, child) {
-      final text = theme.isDarkMode ? appLocale.lightMode : appLocale.darkMode;
+    return Consumer<ThemeSwitchNotifier>(builder: (context, notifier, child) {
+      final text = notifier.isDarkMode ? appLocale.lightMode : appLocale.darkMode;
 
       return Padding(
         padding: const EdgeInsets.symmetric(
@@ -44,9 +44,9 @@ class ToggleThemeButton extends StatelessWidget {
           icon: SvgPicture.asset(kThemeIconAsset),
           label: Text(text),
           onPressed: () {
-            theme.toggleTheme();
+            notifier.toggleTheme();
             AnalyticsService.get(context)
-                .trackClickToggleTheme(!theme.isDarkMode);
+                .trackClickToggleTheme(!notifier.isDarkMode);
           },
         ),
       );
diff --git a/playground/frontend/lib/components/toggle_theme_button/toggle_theme_icon_button.dart b/playground/frontend/lib/components/toggle_theme_button/toggle_theme_icon_button.dart
index a505e4febd6..f82270826fe 100644
--- a/playground/frontend/lib/components/toggle_theme_button/toggle_theme_icon_button.dart
+++ b/playground/frontend/lib/components/toggle_theme_button/toggle_theme_icon_button.dart
@@ -28,12 +28,12 @@ class ToggleThemeIconButton extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return Consumer<ThemeProvider>(builder: (context, theme, child) {
+    return Consumer<ThemeSwitchNotifier>(builder: (context, notifier, child) {
       return IconButton(
         iconSize: kIconSizeLg,
         splashRadius: kIconButtonSplashRadius,
         icon: SvgPicture.asset(kThemeIconAsset),
-        onPressed: theme.toggleTheme,
+        onPressed: notifier.toggleTheme,
       );
     });
   }
diff --git a/playground/frontend/lib/config/theme.dart b/playground/frontend/lib/config/theme.dart
index 607135e0b4e..62a30f0082b 100644
--- a/playground/frontend/lib/config/theme.dart
+++ b/playground/frontend/lib/config/theme.dart
@@ -16,20 +16,46 @@
  * limitations under the License.
  */
 
+import 'package:code_text_field/code_text_field.dart';
 import 'package:flutter/material.dart';
 import 'package:playground/constants/colors.dart';
 import 'package:playground/constants/font_weight.dart';
 import 'package:playground/constants/fonts.dart';
 import 'package:playground/constants/sizes.dart';
+import 'package:playground/modules/editor/components/editor_themes.dart';
 import 'package:provider/provider.dart';
 import 'package:shared_preferences/shared_preferences.dart';
 
 const kThemeMode = 'theme_mode';
 
-class ThemeProvider extends ChangeNotifier {
+class ThemeSwitchNotifier extends ChangeNotifier {
   late SharedPreferences _preferences;
   ThemeMode themeMode = ThemeMode.light;
 
+  static const _darkThemeColors = ThemeColors.fromBrightness(isDark: true);
+  static const _lightThemeColors = ThemeColors.fromBrightness(isDark: false);
+
+  ThemeColors get themeColors {
+    switch (themeMode) {
+      case ThemeMode.dark:
+        return _darkThemeColors;
+      default:
+        return _lightThemeColors;
+    }
+  }
+
+  final _darkCodeTheme = createTheme(_darkThemeColors);
+  final _lightCodeTheme = createTheme(_lightThemeColors);
+
+  CodeThemeData get codeTheme {
+    switch (themeMode) {
+      case ThemeMode.dark:
+        return _darkCodeTheme;
+      default:
+        return _lightCodeTheme;
+    }
+  }
+
   init() {
     _setPreferences();
   }
@@ -53,6 +79,47 @@ class ThemeProvider extends ChangeNotifier {
   }
 }
 
+class ThemeSwitchNotifierProvider extends StatelessWidget {
+  final Widget child;
+
+  const ThemeSwitchNotifierProvider({
+    super.key,
+    required this.child,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return ChangeNotifierProvider<ThemeSwitchNotifier>(
+      create: (context) => ThemeSwitchNotifier()..init(),
+      child: Consumer<ThemeSwitchNotifier>(
+        builder: (context, themeSwitchNotifier, _) => ThemeColorsProvider(
+          data: themeSwitchNotifier.themeColors,
+          child: child,
+        ),
+      ),
+    );
+  }
+}
+
+class ThemeColorsProvider extends StatelessWidget {
+  final ThemeColors data;
+  final Widget child;
+
+  const ThemeColorsProvider({
+    super.key,
+    required this.data,
+    required this.child,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Provider<ThemeColors>.value(
+      value: data,
+      child: child,
+    );
+  }
+}
+
 TextTheme createTextTheme(Color textColor) {
   return getBaseFontTheme(
     const TextTheme(
@@ -174,16 +241,42 @@ final kDarkTheme = ThemeData(
 );
 
 class ThemeColors {
+  final Color? _background;
+  final Color? _dropdownButton;
+
   final bool isDark;
 
-  static ThemeColors of(BuildContext context) {
-    final theme = Provider.of<ThemeProvider>(context);
-    return ThemeColors(theme.isDarkMode);
+  static ThemeColors of(BuildContext context, {bool listen = true}) {
+    return Provider.of<ThemeColors>(context, listen: listen);
+  }
+
+  ThemeColors({
+    required this.isDark,
+    Color? background,
+    Color? dropdownButtonColor,
+  })  : _background = background,
+        _dropdownButton = dropdownButtonColor;
+
+  const ThemeColors.fromBrightness({
+    required this.isDark,
+  })  : _background = null,
+        _dropdownButton = null;
+
+  ThemeColors copyWith({
+    Color? background,
+    Color? dropdownButton,
+  }) {
+    return ThemeColors(
+      isDark: isDark,
+      background: background ?? this.background,
+      dropdownButtonColor: dropdownButton ?? this.dropdownButton,
+    );
   }
 
-  ThemeColors(this.isDark);
+  Color get dropdownButton =>
+      _dropdownButton ?? (isDark ? kDarkGrey : kLightGrey);
 
-  Color get greyColor => isDark ? kDarkGrey : kLightGrey;
+  Color get divider => isDark ? kDarkGrey : kLightGrey;
 
   Color get lightGreyColor => isDark ? kLightGrey1 : kLightGrey;
 
@@ -198,8 +291,9 @@ class ThemeColors {
   Color get secondaryBackground =>
       isDark ? kDarkSecondaryBackground : kLightSecondaryBackground;
 
-  Color get primaryBackground =>
-      isDark ? kDarkPrimaryBackground : kLightPrimaryBackground;
+  Color get background =>
+      _background ??
+      (isDark ? kDarkPrimaryBackground : kLightPrimaryBackground);
 
   Color get code1 => isDark ? kDarkCode2 : kLightCode2;
 
diff --git a/playground/frontend/lib/constants/params.dart b/playground/frontend/lib/constants/params.dart
index d4d6e3fb8b0..82005b6e510 100644
--- a/playground/frontend/lib/constants/params.dart
+++ b/playground/frontend/lib/constants/params.dart
@@ -18,8 +18,12 @@
 
 const kExampleParam = 'example';
 const kIsEditableParam = 'editable';
-const kSourceCode = 'code';
 const kContextLine = 'line';
-const kIsEmbedded = 'embedded';
 
 const kQuickStartCategoryName = 'quick start';
+
+const _kIsEmbedded = 'embedded';
+
+bool isEmbedded() {
+  return Uri.base.toString().contains(_kIsEmbedded);
+}
diff --git a/playground/frontend/lib/constants/sizes.dart b/playground/frontend/lib/constants/sizes.dart
index 74f61ae307a..3d895aae285 100644
--- a/playground/frontend/lib/constants/sizes.dart
+++ b/playground/frontend/lib/constants/sizes.dart
@@ -61,3 +61,7 @@ const double kTitleFontSize = 18.0;
 //divider size
 const double kDividerHeight = 1.0;
 const double kLgDividerHeight = 2.0;
+
+//loading indicator size
+const double kMdLoadingIndicatorSize = 40.0;
+const double kLgLoadingIndicatorSize = 50.0;
diff --git a/playground/frontend/lib/l10n/app_en.arb b/playground/frontend/lib/l10n/app_en.arb
index 5eeb4421eb5..37f0bbbe41e 100644
--- a/playground/frontend/lib/l10n/app_en.arb
+++ b/playground/frontend/lib/l10n/app_en.arb
@@ -226,5 +226,25 @@
   "result": "Result",
   "@result": {
     "description": "Name for the output tab"
+  },
+  "shareMyCode": "Share my code",
+  "@shareMyCode": {
+    "description": "Text for the share button"
+  },
+  "link": "Link",
+  "@link": {
+    "description": "Name of the Link tab in Share dropdown"
+  },
+  "embed": "Embed",
+  "@embed": {
+    "description": "Name of the Embed tab in Share dropdown"
+  },
+  "linkReady": "Link is ready! Anyone with this link can view your example.",
+  "@linkReady": {
+    "description": "Text that appears when user sees link to code sample"
+  },
+  "iframeCodeReady": "With this code you can embed Playground to your website.",
+  "@iframeCodeReady": {
+    "description": "Text that appears when user sees Playground iframe code"
   }
-}
\ No newline at end of file
+}
diff --git a/playground/frontend/lib/modules/editor/components/editor_themes.dart b/playground/frontend/lib/modules/editor/components/editor_themes.dart
index e29f1d6bfe4..42c68905f96 100644
--- a/playground/frontend/lib/modules/editor/components/editor_themes.dart
+++ b/playground/frontend/lib/modules/editor/components/editor_themes.dart
@@ -29,7 +29,7 @@ CodeThemeData createTheme(ThemeColors colors) {
 Map<String, TextStyle> _createThemeStyles(ThemeColors colors) {
   return {
     'root': TextStyle(
-      backgroundColor: colors.primaryBackground,
+      backgroundColor: colors.background,
       color: colors.textColor,
     ),
     'comment': TextStyle(color: colors.codeComment),
@@ -62,6 +62,3 @@ Map<String, TextStyle> _createThemeStyles(ThemeColors colors) {
     'strong': const TextStyle(fontWeight: FontWeight.bold),
   };
 }
-
-final kDarkCodeTheme = createTheme(ThemeColors(true));
-final kLightCodeTheme = createTheme(ThemeColors(false));
diff --git a/playground/frontend/lib/modules/editor/components/share_dropdown/link_text_field.dart b/playground/frontend/lib/modules/editor/components/share_dropdown/link_text_field.dart
new file mode 100644
index 00000000000..9708a88208a
--- /dev/null
+++ b/playground/frontend/lib/modules/editor/components/share_dropdown/link_text_field.dart
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:playground/config/theme.dart';
+import 'package:playground/constants/font_weight.dart';
+import 'package:playground/constants/sizes.dart';
+
+const _kTextFieldMaxHeight = 45.0;
+
+class LinkTextField extends StatefulWidget {
+  final String text;
+
+  const LinkTextField({super.key, required this.text});
+
+  @override
+  State<LinkTextField> createState() => _LinkTextFieldState();
+}
+
+class _LinkTextFieldState extends State<LinkTextField> {
+  final textEditingController = TextEditingController();
+  bool _isPressed = false;
+
+  @override
+  initState() {
+    super.initState();
+    textEditingController.text = widget.text;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      decoration: BoxDecoration(
+        color: ThemeColors.of(context).dropdownButton,
+        borderRadius: BorderRadius.circular(kSmBorderRadius),
+      ),
+      child: Container(
+        margin: const EdgeInsets.symmetric(horizontal: kMdSpacing),
+        child: Center(
+          child: TextFormField(
+            controller: textEditingController,
+            decoration: InputDecoration(
+              constraints: const BoxConstraints(
+                maxHeight: _kTextFieldMaxHeight,
+              ),
+              border: InputBorder.none,
+              suffixIcon: _buildCopyButton(),
+            ),
+            readOnly: true,
+            style: TextStyle(
+              fontSize: kLabelFontSize,
+              fontWeight: kNormalWeight,
+              color: ThemeColors.of(context).primary,
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  Widget _buildCopyButton() {
+    return MouseRegion(
+      cursor: SystemMouseCursors.click,
+      child: GestureDetector(
+        onTap: () async {
+          await _copyLinkText();
+          setState(() {
+            _isPressed = true;
+          });
+        },
+        child: _isPressed
+            ? const Icon(
+                Icons.check,
+                size: kIconSizeMd,
+              )
+            : const Icon(
+                Icons.file_copy_outlined,
+                size: kIconSizeSm,
+              ),
+      ),
+    );
+  }
+
+  Future<void> _copyLinkText() async {
+    await Clipboard.setData(ClipboardData(text: widget.text));
+  }
+}
diff --git a/playground/frontend/lib/modules/editor/components/share_dropdown/share_button.dart b/playground/frontend/lib/modules/editor/components/share_dropdown/share_button.dart
new file mode 100644
index 00000000000..9355934e718
--- /dev/null
+++ b/playground/frontend/lib/modules/editor/components/share_dropdown/share_button.dart
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import 'package:flutter/material.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+import 'package:playground/components/dropdown_button/dropdown_button.dart';
+import 'package:playground/config/theme.dart';
+import 'package:playground/modules/editor/components/share_dropdown/share_dropdown_body.dart';
+
+const _kShareDropdownHeight = 140.0;
+const _kShareDropdownWidth = 460.0;
+const _kButtonColorOpacity = 0.2;
+
+class ShareButton extends StatelessWidget {
+  const ShareButton({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    final appLocale = AppLocalizations.of(context)!;
+    final parentThemeData = ThemeColors.of(context);
+
+    final themeData = parentThemeData.copyWith(
+      background: parentThemeData.secondaryBackground,
+      dropdownButton: parentThemeData.primary.withOpacity(_kButtonColorOpacity),
+    );
+
+    return ThemeColorsProvider(
+      data: themeData,
+      child: AppDropdownButton(
+        buttonText: Text(appLocale.shareMyCode),
+        showArrow: false,
+        leading: Icon(
+          Icons.share_outlined,
+          color: ThemeColors.of(context).primary,
+        ),
+        height: _kShareDropdownHeight,
+        width: _kShareDropdownWidth,
+        dropdownAlign: DropdownAlignment.right,
+        createDropdown: (close) => const ShareDropdownBody(),
+      ),
+    );
+  }
+}
diff --git a/playground/frontend/lib/modules/output/components/output_header/output_header.dart b/playground/frontend/lib/modules/editor/components/share_dropdown/share_dropdown_body.dart
similarity index 50%
copy from playground/frontend/lib/modules/output/components/output_header/output_header.dart
copy to playground/frontend/lib/modules/editor/components/share_dropdown/share_dropdown_body.dart
index 95988ee6fcc..68a20837367 100644
--- a/playground/frontend/lib/modules/output/components/output_header/output_header.dart
+++ b/playground/frontend/lib/modules/editor/components/share_dropdown/share_dropdown_body.dart
@@ -17,40 +17,42 @@
  */
 
 import 'package:flutter/material.dart';
-import 'package:playground/constants/sizes.dart';
-import 'package:playground/modules/output/components/output_header/output_placements.dart';
+import 'package:playground/modules/editor/components/share_dropdown/share_tabs/share_tabs.dart';
+import 'package:playground/modules/editor/components/share_dropdown/share_tabs_headers.dart';
+import 'package:playground/modules/output/components/output_header/tab_header.dart';
 
-import 'output_tabs.dart';
+const _kTabsCount = 2;
 
-class OutputHeader extends StatelessWidget {
-  final TabController tabController;
-  final bool showOutputPlacements;
-  final bool showGraph;
+class ShareDropdownBody extends StatefulWidget {
+  const ShareDropdownBody({super.key});
 
-  const OutputHeader({
-    Key? key,
-    required this.tabController,
-    this.showOutputPlacements = true,
-    this.showGraph = true,
-  }) : super(key: key);
+  @override
+  State<ShareDropdownBody> createState() => _ShareDropdownBodyState();
+}
+
+class _ShareDropdownBodyState extends State<ShareDropdownBody>
+    with SingleTickerProviderStateMixin {
+  late final tabController = TabController(vsync: this, length: _kTabsCount);
+
+  @override
+  void dispose() {
+    tabController.dispose();
+    super.dispose();
+  }
 
   @override
   Widget build(BuildContext context) {
-    return SizedBox(
-      height: 50,
-      child: Padding(
-        padding: const EdgeInsets.symmetric(
-          horizontal: kXlSpacing,
-          vertical: kZeroSpacing,
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        TabHeader(
+          tabController: tabController,
+          tabsWidget: ShareTabsHeaders(tabController: tabController),
         ),
-        child: Row(
-          mainAxisAlignment: MainAxisAlignment.spaceBetween,
-          children: [
-            OutputTabs(tabController: tabController, showGraph: showGraph),
-            showOutputPlacements ? const OutputPlacements() : const SizedBox(),
-          ],
+        Expanded(
+          child: ShareTabs(tabController: tabController),
         ),
-      ),
+      ],
     );
   }
 }
diff --git a/playground/frontend/lib/components/toggle_theme_button/toggle_theme_icon_button.dart b/playground/frontend/lib/modules/editor/components/share_dropdown/share_tab_body.dart
similarity index 62%
copy from playground/frontend/lib/components/toggle_theme_button/toggle_theme_icon_button.dart
copy to playground/frontend/lib/modules/editor/components/share_dropdown/share_tab_body.dart
index a505e4febd6..0055c9dc07b 100644
--- a/playground/frontend/lib/components/toggle_theme_button/toggle_theme_icon_button.dart
+++ b/playground/frontend/lib/modules/editor/components/share_dropdown/share_tab_body.dart
@@ -17,24 +17,28 @@
  */
 
 import 'package:flutter/material.dart';
-import 'package:flutter_svg/flutter_svg.dart';
-import 'package:playground/config/theme.dart';
-import 'package:playground/constants/assets.dart';
 import 'package:playground/constants/sizes.dart';
-import 'package:provider/provider.dart';
 
-class ToggleThemeIconButton extends StatelessWidget {
-  const ToggleThemeIconButton({Key? key}) : super(key: key);
+class ShareTabBody extends StatelessWidget {
+  final List<Widget> children;
+
+  const ShareTabBody({
+    super.key,
+    required this.children,
+  });
 
   @override
   Widget build(BuildContext context) {
-    return Consumer<ThemeProvider>(builder: (context, theme, child) {
-      return IconButton(
-        iconSize: kIconSizeLg,
-        splashRadius: kIconButtonSplashRadius,
-        icon: SvgPicture.asset(kThemeIconAsset),
-        onPressed: theme.toggleTheme,
-      );
-    });
+    return Padding(
+      padding: const EdgeInsets.symmetric(
+        horizontal: kXlSpacing,
+      ),
+      child: Center(
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+          children: children,
+        ),
+      ),
+    );
   }
 }
diff --git a/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/example_share_tabs.dart b/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/example_share_tabs.dart
new file mode 100644
index 00000000000..3a7747844e5
--- /dev/null
+++ b/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/example_share_tabs.dart
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import 'package:flutter/material.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+import 'package:playground/modules/editor/components/share_dropdown/link_text_field.dart';
+import 'package:playground/modules/editor/components/share_dropdown/share_tab_body.dart';
+import 'package:playground/utils/share_code_utils.dart';
+
+class ExampleShareTabs extends StatelessWidget {
+  final String examplePath;
+  final TabController tabController;
+
+  const ExampleShareTabs({
+    super.key,
+    required this.examplePath,
+    required this.tabController,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    final appLocale = AppLocalizations.of(context)!;
+
+    return TabBarView(
+      controller: tabController,
+      physics: const NeverScrollableScrollPhysics(),
+      children: [
+        ShareTabBody(
+          children: [
+            Text(appLocale.linkReady),
+            LinkTextField(
+              text: ShareCodeUtils.examplePathToPlaygroundUrl(
+                examplePath: examplePath,
+                view: PlaygroundView.standalone,
+              ).toString(),
+            ),
+          ],
+        ),
+        ShareTabBody(
+          children: [
+            Text(appLocale.iframeCodeReady),
+            LinkTextField(
+              text: ShareCodeUtils.examplePathToIframeCode(
+                examplePath: examplePath,
+              ),
+            ),
+          ],
+        ),
+      ],
+    );
+  }
+}
diff --git a/playground/frontend/lib/modules/output/components/output_header/output_header.dart b/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/share_tabs.dart
similarity index 51%
copy from playground/frontend/lib/modules/output/components/output_header/output_header.dart
copy to playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/share_tabs.dart
index 95988ee6fcc..8c2e72948ae 100644
--- a/playground/frontend/lib/modules/output/components/output_header/output_header.dart
+++ b/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/share_tabs.dart
@@ -17,39 +17,37 @@
  */
 
 import 'package:flutter/material.dart';
-import 'package:playground/constants/sizes.dart';
-import 'package:playground/modules/output/components/output_header/output_placements.dart';
+import 'package:playground/modules/editor/components/share_dropdown/share_tabs/example_share_tabs.dart';
+import 'package:playground/modules/editor/components/share_dropdown/share_tabs/snippet_save_and_share_tabs.dart';
+import 'package:playground/pages/playground/states/playground_state.dart';
+import 'package:provider/provider.dart';
 
-import 'output_tabs.dart';
-
-class OutputHeader extends StatelessWidget {
+class ShareTabs extends StatelessWidget {
   final TabController tabController;
-  final bool showOutputPlacements;
-  final bool showGraph;
 
-  const OutputHeader({
-    Key? key,
+  const ShareTabs({
+    super.key,
     required this.tabController,
-    this.showOutputPlacements = true,
-    this.showGraph = true,
-  }) : super(key: key);
+  });
 
   @override
   Widget build(BuildContext context) {
-    return SizedBox(
-      height: 50,
-      child: Padding(
-        padding: const EdgeInsets.symmetric(
-          horizontal: kXlSpacing,
-          vertical: kZeroSpacing,
-        ),
-        child: Row(
-          mainAxisAlignment: MainAxisAlignment.spaceBetween,
-          children: [
-            OutputTabs(tabController: tabController, showGraph: showGraph),
-            showOutputPlacements ? const OutputPlacements() : const SizedBox(),
-          ],
-        ),
+    return Container(
+      color: Theme.of(context).backgroundColor,
+      child: Consumer<PlaygroundState>(
+        builder: (context, playgroundState, _) {
+          if (playgroundState.isExampleChanged) {
+            return SnippetSaveAndShareTabs(
+              playgroundState: playgroundState,
+              tabController: tabController,
+            );
+          }
+
+          return ExampleShareTabs(
+            examplePath: playgroundState.selectedExample!.path,
+            tabController: tabController,
+          );
+        },
       ),
     );
   }
diff --git a/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/snippet_save_and_share_tabs.dart b/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/snippet_save_and_share_tabs.dart
new file mode 100644
index 00000000000..f8b606c81aa
--- /dev/null
+++ b/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/snippet_save_and_share_tabs.dart
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import 'package:flutter/material.dart';
+import 'package:playground/components/loading_indicator/loading_indicator.dart';
+import 'package:playground/constants/sizes.dart';
+import 'package:playground/modules/editor/components/share_dropdown/share_tabs/example_share_tabs.dart';
+import 'package:playground/modules/examples/repositories/models/shared_file_model.dart';
+import 'package:playground/pages/playground/states/playground_state.dart';
+
+class SnippetSaveAndShareTabs extends StatelessWidget {
+  final PlaygroundState playgroundState;
+  final TabController tabController;
+
+  const SnippetSaveAndShareTabs({
+    super.key,
+    required this.playgroundState,
+    required this.tabController,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return FutureBuilder(
+      future: playgroundState.exampleState.getSnippetId(
+        files: [SharedFile(code: playgroundState.source, isMain: true)],
+        sdk: playgroundState.sdk,
+        pipelineOptions: playgroundState.pipelineOptions,
+      ),
+      builder: (context, snapshot) {
+        if (!snapshot.hasData) {
+          return const LoadingIndicator(size: kLgLoadingIndicatorSize);
+        }
+
+        return ExampleShareTabs(
+          examplePath: snapshot.data.toString(),
+          tabController: tabController,
+        );
+      },
+    );
+  }
+}
diff --git a/playground/frontend/lib/components/toggle_theme_button/toggle_theme_icon_button.dart b/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs_headers.dart
similarity index 58%
copy from playground/frontend/lib/components/toggle_theme_button/toggle_theme_icon_button.dart
copy to playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs_headers.dart
index a505e4febd6..4d80b4309bd 100644
--- a/playground/frontend/lib/components/toggle_theme_button/toggle_theme_icon_button.dart
+++ b/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs_headers.dart
@@ -17,23 +17,34 @@
  */
 
 import 'package:flutter/material.dart';
-import 'package:flutter_svg/flutter_svg.dart';
-import 'package:playground/config/theme.dart';
-import 'package:playground/constants/assets.dart';
-import 'package:playground/constants/sizes.dart';
+import 'package:playground/pages/playground/states/playground_state.dart';
 import 'package:provider/provider.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
 
-class ToggleThemeIconButton extends StatelessWidget {
-  const ToggleThemeIconButton({Key? key}) : super(key: key);
+const _width = 180.0;
+
+class ShareTabsHeaders extends StatelessWidget {
+  final TabController tabController;
+
+  const ShareTabsHeaders({
+    super.key,
+    required this.tabController,
+  });
 
   @override
   Widget build(BuildContext context) {
-    return Consumer<ThemeProvider>(builder: (context, theme, child) {
-      return IconButton(
-        iconSize: kIconSizeLg,
-        splashRadius: kIconButtonSplashRadius,
-        icon: SvgPicture.asset(kThemeIconAsset),
-        onPressed: theme.toggleTheme,
+    final appLocale = AppLocalizations.of(context)!;
+
+    return Consumer<PlaygroundState>(builder: (context, state, child) {
+      return SizedBox(
+        width: _width,
+        child: TabBar(
+          controller: tabController,
+          tabs: [
+            Text(appLocale.link),
+            Text(appLocale.embed),
+          ],
+        ),
       );
     });
   }
diff --git a/playground/frontend/lib/modules/examples/components/example_list/expansion_panel_item.dart b/playground/frontend/lib/modules/examples/components/example_list/expansion_panel_item.dart
index 643c4585d96..cf513333903 100644
--- a/playground/frontend/lib/modules/examples/components/example_list/expansion_panel_item.dart
+++ b/playground/frontend/lib/modules/examples/components/example_list/expansion_panel_item.dart
@@ -41,18 +41,16 @@ class ExpansionPanelItem extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return Consumer2<PlaygroundState, ExampleState>(
-      builder: (context, playgroundState, exampleState, child) => MouseRegion(
+    return Consumer<PlaygroundState>(
+      builder: (context, playgroundState, child) => MouseRegion(
         cursor: SystemMouseCursors.click,
         child: GestureDetector(
           onTap: () async {
             if (playgroundState.selectedExample != example) {
-              closeDropdown(exampleState);
+              _closeDropdown(playgroundState.exampleState);
               AnalyticsService.get(context).trackSelectExample(example);
-              final exampleWithInfo = await exampleState.loadExampleInfo(
-                example,
-                playgroundState.sdk,
-              );
+              final exampleWithInfo =
+                  await playgroundState.exampleState.loadExampleInfo(example);
               playgroundState.setExample(exampleWithInfo);
             }
           },
@@ -82,7 +80,7 @@ class ExpansionPanelItem extends StatelessWidget {
     );
   }
 
-  void closeDropdown(ExampleState exampleState) {
+  void _closeDropdown(ExampleState exampleState) {
     animationController.reverse();
     dropdown?.remove();
     exampleState.changeSelectorVisibility();
diff --git a/playground/frontend/lib/modules/examples/example_selector.dart b/playground/frontend/lib/modules/examples/example_selector.dart
index 0e1c359a1d9..480852470a8 100644
--- a/playground/frontend/lib/modules/examples/example_selector.dart
+++ b/playground/frontend/lib/modules/examples/example_selector.dart
@@ -18,6 +18,7 @@
 
 import 'package:flutter/material.dart';
 import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+import 'package:playground/components/horizonta_divider/horizontal_divider.dart';
 import 'package:playground/components/loading_indicator/loading_indicator.dart';
 import 'package:playground/config/theme.dart';
 import 'package:playground/constants/links.dart';
@@ -25,10 +26,10 @@ import 'package:playground/constants/sizes.dart';
 import 'package:playground/modules/examples/components/examples_components.dart';
 import 'package:playground/modules/examples/components/outside_click_handler.dart';
 import 'package:playground/modules/examples/models/popover_state.dart';
-import 'package:playground/modules/examples/models/selector_size_model.dart';
 import 'package:playground/pages/playground/states/example_selector_state.dart';
 import 'package:playground/pages/playground/states/examples_state.dart';
 import 'package:playground/pages/playground/states/playground_state.dart';
+import 'package:playground/utils/dropdown_utils.dart';
 import 'package:provider/provider.dart';
 import 'package:url_launcher/url_launcher.dart';
 
@@ -89,7 +90,7 @@ class _ExampleSelectorState extends State<ExampleSelector>
     return Container(
       height: kContainerHeight,
       decoration: BoxDecoration(
-        color: ThemeColors.of(context).greyColor,
+        color: ThemeColors.of(context).dropdownButton,
         borderRadius: BorderRadius.circular(kSmBorderRadius),
       ),
       child: Consumer<PlaygroundState>(
@@ -122,34 +123,35 @@ class _ExampleSelectorState extends State<ExampleSelector>
   }
 
   OverlayEntry createExamplesDropdown() {
-    SelectorPositionModel posModel = findSelectorPositionData();
+    Offset dropdownOffset = findDropdownOffset(key: selectorKey);
 
     return OverlayEntry(
       builder: (context) {
         return ChangeNotifierProvider<PopoverState>(
           create: (context) => PopoverState(false),
           builder: (context, state) {
-            return Consumer2<ExampleState, PlaygroundState>(
-              builder: (context, exampleState, playgroundState, child) => Stack(
+            return Consumer<PlaygroundState>(
+              builder: (context, playgroundState, child) => Stack(
                 children: [
                   OutsideClickHandler(
                     onTap: () {
-                      closeDropdown(exampleState);
+                      _closeDropdown(playgroundState.exampleState);
                       // handle description dialogs
-                      Navigator.of(context, rootNavigator: true).popUntil((route) {
+                      Navigator.of(context, rootNavigator: true)
+                          .popUntil((route) {
                         return route.isFirst;
                       });
                     },
                   ),
                   ChangeNotifierProvider(
                     create: (context) => ExampleSelectorState(
-                      exampleState,
                       playgroundState,
-                      exampleState.getCategories(playgroundState.sdk)!,
+                      playgroundState.exampleState
+                          .getCategories(playgroundState.sdk)!,
                     ),
                     builder: (context, _) => Positioned(
-                      left: posModel.xAlignment,
-                      top: posModel.yAlignment + kAdditionalDyAlignment,
+                      left: dropdownOffset.dx,
+                      top: dropdownOffset.dy,
                       child: SlideTransition(
                         position: offsetAnimation,
                         child: Material(
@@ -159,12 +161,13 @@ class _ExampleSelectorState extends State<ExampleSelector>
                             width: kLgContainerWidth,
                             decoration: BoxDecoration(
                               color: Theme.of(context).backgroundColor,
-                              borderRadius: BorderRadius.circular(kMdBorderRadius),
+                              borderRadius:
+                                  BorderRadius.circular(kMdBorderRadius),
+                            ),
+                            child: _buildDropdownContent(
+                              context,
+                              playgroundState,
                             ),
-                            child: exampleState.sdkCategories == null ||
-                                    playgroundState.selectedExample == null
-                                ? const LoadingIndicator(size: kContainerHeight)
-                                : _buildDropdownContent(context, playgroundState),
                           ),
                         ),
                       ),
@@ -173,7 +176,7 @@ class _ExampleSelectorState extends State<ExampleSelector>
                 ],
               ),
             );
-          }
+          },
         );
       },
     );
@@ -183,6 +186,13 @@ class _ExampleSelectorState extends State<ExampleSelector>
     BuildContext context,
     PlaygroundState playgroundState,
   ) {
+    if (playgroundState.exampleState.sdkCategories == null ||
+        playgroundState.selectedExample == null) {
+      return const LoadingIndicator(
+        size: kMdLoadingIndicatorSize,
+      );
+    }
+
     return Column(
       children: [
         SearchField(controller: textController),
@@ -193,12 +203,7 @@ class _ExampleSelectorState extends State<ExampleSelector>
           animationController: animationController,
           dropdown: examplesDropdown,
         ),
-        Divider(
-          height: kDividerHeight,
-          color: ThemeColors.of(context).greyColor,
-          indent: kLgSpacing,
-          endIndent: kLgSpacing,
-        ),
+        const HorizontalDivider(indent: kLgSpacing),
         SizedBox(
           width: double.infinity,
           child: TextButton(
@@ -214,22 +219,12 @@ class _ExampleSelectorState extends State<ExampleSelector>
             ),
             onPressed: () => launchUrl(Uri.parse(kAddExampleLink)),
           ),
-        )
+        ),
       ],
     );
   }
 
-  SelectorPositionModel findSelectorPositionData() {
-    RenderBox? rBox =
-        selectorKey.currentContext?.findRenderObject() as RenderBox;
-    SelectorPositionModel positionModel = SelectorPositionModel(
-      xAlignment: rBox.localToGlobal(Offset.zero).dx,
-      yAlignment: rBox.localToGlobal(Offset.zero).dy,
-    );
-    return positionModel;
-  }
-
-  void closeDropdown(ExampleState exampleState) {
+  void _closeDropdown(ExampleState exampleState) {
     animationController.reverse();
     examplesDropdown?.remove();
     exampleState.changeSelectorVisibility();
diff --git a/playground/frontend/lib/modules/examples/models/selector_size_model.dart b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart
similarity index 55%
copy from playground/frontend/lib/modules/examples/models/selector_size_model.dart
copy to playground/frontend/lib/modules/examples/models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart
index 27954f7ae22..0bdc18e01c7 100644
--- a/playground/frontend/lib/modules/examples/models/selector_size_model.dart
+++ b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart
@@ -16,12 +16,25 @@
  * limitations under the License.
  */
 
-class SelectorPositionModel {
-  final double xAlignment;
-  final double yAlignment;
+import 'package:playground/modules/sdk/models/sdk.dart';
+import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_origin.dart';
 
-  const SelectorPositionModel({
-    required this.xAlignment,
-    required this.yAlignment,
+class CatalogDefaultExampleLoadingDescriptor extends ExampleLoadingDescriptor {
+  final SDK sdk;
+
+  const CatalogDefaultExampleLoadingDescriptor({
+    required this.sdk,
   });
+
+  @override
+  ExampleOrigin get origin => ExampleOrigin.catalogDefault;
+
+  @override
+  int get hashCode => sdk.hashCode;
+
+  @override
+  bool operator ==(Object other) {
+    return other is CatalogDefaultExampleLoadingDescriptor && sdk == other.sdk;
+  }
 }
diff --git a/playground/frontend/lib/modules/examples/models/selector_size_model.dart b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/empty_example_loading_descriptor.dart
similarity index 56%
copy from playground/frontend/lib/modules/examples/models/selector_size_model.dart
copy to playground/frontend/lib/modules/examples/models/example_loading_descriptors/empty_example_loading_descriptor.dart
index 27954f7ae22..5a7fa46dce0 100644
--- a/playground/frontend/lib/modules/examples/models/selector_size_model.dart
+++ b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/empty_example_loading_descriptor.dart
@@ -16,12 +16,25 @@
  * limitations under the License.
  */
 
-class SelectorPositionModel {
-  final double xAlignment;
-  final double yAlignment;
+import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_origin.dart';
+import 'package:playground/modules/sdk/models/sdk.dart';
 
-  const SelectorPositionModel({
-    required this.xAlignment,
-    required this.yAlignment,
+class EmptyExampleLoadingDescriptor extends ExampleLoadingDescriptor {
+  final SDK sdk;
+
+  const EmptyExampleLoadingDescriptor({
+    required this.sdk,
   });
+
+  @override
+  ExampleOrigin get origin => ExampleOrigin.empty;
+
+  @override
+  int get hashCode => sdk.hashCode;
+
+  @override
+  bool operator ==(Object other) {
+    return other is EmptyExampleLoadingDescriptor && sdk == other.sdk;
+  }
 }
diff --git a/playground/frontend/lib/modules/examples/models/selector_size_model.dart b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart
similarity index 78%
copy from playground/frontend/lib/modules/examples/models/selector_size_model.dart
copy to playground/frontend/lib/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart
index 27954f7ae22..539c3feaf20 100644
--- a/playground/frontend/lib/modules/examples/models/selector_size_model.dart
+++ b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart
@@ -16,12 +16,13 @@
  * limitations under the License.
  */
 
-class SelectorPositionModel {
-  final double xAlignment;
-  final double yAlignment;
+import 'package:playground/modules/examples/models/example_origin.dart';
 
-  const SelectorPositionModel({
-    required this.xAlignment,
-    required this.yAlignment,
-  });
+abstract class ExampleLoadingDescriptor {
+  const ExampleLoadingDescriptor();
+
+  ExampleOrigin get origin;
+
+  @override
+  String toString() => '$origin';
 }
diff --git a/playground/frontend/lib/modules/examples/models/selector_size_model.dart b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart
similarity index 56%
copy from playground/frontend/lib/modules/examples/models/selector_size_model.dart
copy to playground/frontend/lib/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart
index 27954f7ae22..84d6577bdaf 100644
--- a/playground/frontend/lib/modules/examples/models/selector_size_model.dart
+++ b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart
@@ -16,12 +16,26 @@
  * limitations under the License.
  */
 
-class SelectorPositionModel {
-  final double xAlignment;
-  final double yAlignment;
+import 'package:collection/collection.dart';
 
-  const SelectorPositionModel({
-    required this.xAlignment,
-    required this.yAlignment,
+import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart';
+
+class ExamplesLoadingDescriptor {
+  final List<ExampleLoadingDescriptor> descriptors;
+
+  const ExamplesLoadingDescriptor({
+    required this.descriptors,
   });
+
+  @override
+  String toString() => descriptors.map((e) => e.toString()).join('_');
+
+  @override
+  int get hashCode => Object.hashAll(descriptors);
+
+  @override
+  bool operator ==(Object other) {
+    return other is ExamplesLoadingDescriptor &&
+        const ListEquality().equals(descriptors, other.descriptors);
+  }
 }
diff --git a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/examples_loading_descriptor_factory.dart b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/examples_loading_descriptor_factory.dart
new file mode 100644
index 00000000000..b0a07e6f312
--- /dev/null
+++ b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/examples_loading_descriptor_factory.dart
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import 'package:playground/constants/params.dart';
+import 'package:playground/modules/examples/models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_loading_descriptors/empty_example_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_loading_descriptors/standard_example_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_loading_descriptors/user_shared_example_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_token_type.dart';
+import 'package:playground/modules/sdk/models/sdk.dart';
+
+class ExamplesLoadingDescriptorFactory {
+  static ExamplesLoadingDescriptor fromUriParts({
+    required String path,
+    required Map<String, dynamic> params,
+  }) {
+    return _tryParseSingleExample(params) ??
+        _tryParseCatalogDefaultExample() ??
+        _oneEmptyWithDefaultSdk;
+  }
+
+  static ExamplesLoadingDescriptor? _tryParseSingleExample(
+    Map<String, dynamic> params,
+  ) {
+    final token = params[kExampleParam];
+    if (token is! String) {
+      return null;
+    }
+
+    return ExamplesLoadingDescriptor(
+      descriptors: [_parseSingleExample(token)],
+    );
+  }
+
+  static ExamplesLoadingDescriptor? _tryParseCatalogDefaultExample() {
+    if (isEmbedded()) {
+      return null;
+    }
+
+    return const ExamplesLoadingDescriptor(
+      descriptors: [CatalogDefaultExampleLoadingDescriptor(sdk: SDK.java)],
+    );
+  }
+
+  static ExampleLoadingDescriptor _parseSingleExample(String token) {
+    final tokenType = ExampleTokenType.fromToken(token);
+
+    switch (tokenType) {
+      case ExampleTokenType.standard:
+        return StandardExampleLoadingDescriptor(path: token);
+
+      case ExampleTokenType.userShared:
+        return UserSharedExampleLoadingDescriptor(snippetId: token);
+    }
+  }
+
+  static ExamplesLoadingDescriptor get _oneEmptyWithDefaultSdk {
+    return const ExamplesLoadingDescriptor(
+      descriptors: [EmptyExampleLoadingDescriptor(sdk: SDK.java)],
+    );
+  }
+}
diff --git a/playground/frontend/lib/modules/examples/models/selector_size_model.dart b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/standard_example_loading_descriptor.dart
similarity index 55%
copy from playground/frontend/lib/modules/examples/models/selector_size_model.dart
copy to playground/frontend/lib/modules/examples/models/example_loading_descriptors/standard_example_loading_descriptor.dart
index 27954f7ae22..8794fc02ac1 100644
--- a/playground/frontend/lib/modules/examples/models/selector_size_model.dart
+++ b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/standard_example_loading_descriptor.dart
@@ -16,12 +16,27 @@
  * limitations under the License.
  */
 
-class SelectorPositionModel {
-  final double xAlignment;
-  final double yAlignment;
+import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_origin.dart';
 
-  const SelectorPositionModel({
-    required this.xAlignment,
-    required this.yAlignment,
+class StandardExampleLoadingDescriptor extends ExampleLoadingDescriptor {
+  final String path;
+
+  const StandardExampleLoadingDescriptor({
+    required this.path,
   });
+
+  @override
+  ExampleOrigin get origin => ExampleOrigin.standard;
+
+  @override
+  String toString() => '$origin-$path';
+
+  @override
+  int get hashCode => path.hashCode;
+
+  @override
+  bool operator ==(Object other) {
+    return other is StandardExampleLoadingDescriptor && path == other.path;
+  }
 }
diff --git a/playground/frontend/lib/modules/examples/models/selector_size_model.dart b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/user_shared_example_loading_descriptor.dart
similarity index 54%
copy from playground/frontend/lib/modules/examples/models/selector_size_model.dart
copy to playground/frontend/lib/modules/examples/models/example_loading_descriptors/user_shared_example_loading_descriptor.dart
index 27954f7ae22..072445d04f0 100644
--- a/playground/frontend/lib/modules/examples/models/selector_size_model.dart
+++ b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/user_shared_example_loading_descriptor.dart
@@ -16,12 +16,28 @@
  * limitations under the License.
  */
 
-class SelectorPositionModel {
-  final double xAlignment;
-  final double yAlignment;
+import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_origin.dart';
 
-  const SelectorPositionModel({
-    required this.xAlignment,
-    required this.yAlignment,
+class UserSharedExampleLoadingDescriptor extends ExampleLoadingDescriptor {
+  final String snippetId;
+
+  const UserSharedExampleLoadingDescriptor({
+    required this.snippetId,
   });
+
+  @override
+  ExampleOrigin get origin => ExampleOrigin.userShared;
+
+  @override
+  String toString() => '$origin-$snippetId';
+
+  @override
+  int get hashCode => snippetId.hashCode;
+
+  @override
+  bool operator ==(Object other) {
+    return other is UserSharedExampleLoadingDescriptor &&
+        snippetId == other.snippetId;
+  }
 }
diff --git a/playground/frontend/lib/modules/examples/models/example_model.dart b/playground/frontend/lib/modules/examples/models/example_model.dart
index d76b9c28efa..054dddb500a 100644
--- a/playground/frontend/lib/modules/examples/models/example_model.dart
+++ b/playground/frontend/lib/modules/examples/models/example_model.dart
@@ -16,6 +16,8 @@
  * limitations under the License.
  */
 
+import 'package:playground/modules/sdk/models/sdk.dart';
+
 enum ExampleType {
   all,
   example,
@@ -39,6 +41,7 @@ extension ExampleTypeToString on ExampleType {
 }
 
 class ExampleModel with Comparable<ExampleModel> {
+  final SDK sdk;
   final ExampleType type;
   final String name;
   final String path;
@@ -53,6 +56,7 @@ class ExampleModel with Comparable<ExampleModel> {
   String? graph;
 
   ExampleModel({
+    required this.sdk,
     required this.name,
     required this.path,
     required this.description,
diff --git a/playground/frontend/lib/modules/examples/models/selector_size_model.dart b/playground/frontend/lib/modules/examples/models/example_origin.dart
similarity index 68%
copy from playground/frontend/lib/modules/examples/models/selector_size_model.dart
copy to playground/frontend/lib/modules/examples/models/example_origin.dart
index 27954f7ae22..6be4b3b67f6 100644
--- a/playground/frontend/lib/modules/examples/models/selector_size_model.dart
+++ b/playground/frontend/lib/modules/examples/models/example_origin.dart
@@ -16,12 +16,25 @@
  * limitations under the License.
  */
 
-class SelectorPositionModel {
-  final double xAlignment;
-  final double yAlignment;
+import 'package:playground/modules/sdk/models/sdk.dart';
 
-  const SelectorPositionModel({
-    required this.xAlignment,
-    required this.yAlignment,
-  });
+enum ExampleOrigin {
+  empty,
+  standard,
+  userShared,
+  catalogDefault,
+  ;
+
+  static ExampleOrigin fromToken(String? token) {
+    if (token == null) {
+      return empty;
+    }
+
+    final sdk = SDK.tryParseExamplePath(token);
+    if (sdk != null) {
+      return standard;
+    }
+
+    return userShared;
+  }
 }
diff --git a/playground/frontend/lib/modules/examples/models/selector_size_model.dart b/playground/frontend/lib/modules/examples/models/example_token_type.dart
similarity index 73%
copy from playground/frontend/lib/modules/examples/models/selector_size_model.dart
copy to playground/frontend/lib/modules/examples/models/example_token_type.dart
index 27954f7ae22..789b1c3a179 100644
--- a/playground/frontend/lib/modules/examples/models/selector_size_model.dart
+++ b/playground/frontend/lib/modules/examples/models/example_token_type.dart
@@ -16,12 +16,19 @@
  * limitations under the License.
  */
 
-class SelectorPositionModel {
-  final double xAlignment;
-  final double yAlignment;
+import 'package:playground/modules/sdk/models/sdk.dart';
 
-  const SelectorPositionModel({
-    required this.xAlignment,
-    required this.yAlignment,
-  });
+enum ExampleTokenType {
+  standard,
+  userShared,
+  ;
+
+  static ExampleTokenType fromToken(String token) {
+    final sdk = SDK.tryParseExamplePath(token);
+    if (sdk != null) {
+      return standard;
+    }
+
+    return userShared;
+  }
 }
diff --git a/playground/frontend/lib/modules/examples/repositories/example_client/example_client.dart b/playground/frontend/lib/modules/examples/repositories/example_client/example_client.dart
index d63e42cba6a..039a567c5ef 100644
--- a/playground/frontend/lib/modules/examples/repositories/example_client/example_client.dart
+++ b/playground/frontend/lib/modules/examples/repositories/example_client/example_client.dart
@@ -17,18 +17,24 @@
  */
 
 import 'package:playground/modules/editor/repository/code_repository/code_client/output_response.dart';
+import 'package:playground/modules/examples/repositories/models/get_snippet_request.dart';
+import 'package:playground/modules/examples/repositories/models/get_snippet_response.dart';
 import 'package:playground/modules/examples/repositories/models/get_example_code_response.dart';
 import 'package:playground/modules/examples/repositories/models/get_example_request.dart';
 import 'package:playground/modules/examples/repositories/models/get_example_response.dart';
 import 'package:playground/modules/examples/repositories/models/get_list_of_examples_request.dart';
 import 'package:playground/modules/examples/repositories/models/get_list_of_examples_response.dart';
+import 'package:playground/modules/examples/repositories/models/save_snippet_request.dart';
+import 'package:playground/modules/examples/repositories/models/save_snippet_response.dart';
 
 abstract class ExampleClient {
   Future<GetListOfExampleResponse> getListOfExamples(
     GetListOfExamplesRequestWrapper request,
   );
 
-  Future<GetExampleCodeResponse> getExampleSource(GetExampleRequestWrapper request);
+  Future<GetExampleCodeResponse> getExampleSource(
+    GetExampleRequestWrapper request,
+  );
 
   Future<GetExampleResponse> getDefaultExample(
     GetExampleRequestWrapper request,
@@ -38,9 +44,23 @@ abstract class ExampleClient {
     GetExampleRequestWrapper request,
   );
 
-  Future<OutputResponse> getExampleOutput(GetExampleRequestWrapper request);
+  Future<OutputResponse> getExampleOutput(
+    GetExampleRequestWrapper request,
+  );
+
+  Future<OutputResponse> getExampleLogs(
+    GetExampleRequestWrapper request,
+  );
+
+  Future<OutputResponse> getExampleGraph(
+    GetExampleRequestWrapper request,
+  );
 
-  Future<OutputResponse> getExampleLogs(GetExampleRequestWrapper request);
+  Future<GetSnippetResponse> getSnippet(
+    GetSnippetRequestWrapper request,
+  );
 
-  Future<OutputResponse> getExampleGraph(GetExampleRequestWrapper request);
+  Future<SaveSnippetResponse> saveSnippet(
+    SaveSnippetRequestWrapper request,
+  );
 }
diff --git a/playground/frontend/lib/modules/examples/repositories/example_client/grpc_example_client.dart b/playground/frontend/lib/modules/examples/repositories/example_client/grpc_example_client.dart
index 15d381e08e6..f4a11be58aa 100644
--- a/playground/frontend/lib/modules/examples/repositories/example_client/grpc_example_client.dart
+++ b/playground/frontend/lib/modules/examples/repositories/example_client/grpc_example_client.dart
@@ -29,6 +29,11 @@ import 'package:playground/modules/examples/repositories/models/get_example_requ
 import 'package:playground/modules/examples/repositories/models/get_example_response.dart';
 import 'package:playground/modules/examples/repositories/models/get_list_of_examples_request.dart';
 import 'package:playground/modules/examples/repositories/models/get_list_of_examples_response.dart';
+import 'package:playground/modules/examples/repositories/models/get_snippet_request.dart';
+import 'package:playground/modules/examples/repositories/models/get_snippet_response.dart';
+import 'package:playground/modules/examples/repositories/models/save_snippet_request.dart';
+import 'package:playground/modules/examples/repositories/models/save_snippet_response.dart';
+import 'package:playground/modules/examples/repositories/models/shared_file_model.dart';
 import 'package:playground/modules/sdk/models/sdk.dart';
 import 'package:playground/utils/replace_incorrect_symbols.dart';
 
@@ -63,8 +68,14 @@ class GrpcExampleClient implements ExampleClient {
       () => _defaultClient
           .getDefaultPrecompiledObject(
               _getDefaultExampleRequestToGrpcRequest(request))
-          .then((response) =>
-              GetExampleResponse(_toExampleModel(response.precompiledObject))),
+          .then(
+            (response) => GetExampleResponse(
+              _toExampleModel(
+                request.sdk,
+                response.precompiledObject,
+              ),
+            ),
+          ),
     );
   }
 
@@ -76,8 +87,14 @@ class GrpcExampleClient implements ExampleClient {
       () => _defaultClient
           .getPrecompiledObject(
               grpc.GetPrecompiledObjectRequest()..cloudPath = request.path)
-          .then((response) =>
-              GetExampleResponse(_toExampleModel(response.precompiledObject))),
+          .then(
+            (response) => GetExampleResponse(
+              _toExampleModel(
+                request.sdk,
+                response.precompiledObject,
+              ),
+            ),
+          ),
     );
   }
 
@@ -136,6 +153,38 @@ class GrpcExampleClient implements ExampleClient {
     );
   }
 
+  @override
+  Future<GetSnippetResponse> getSnippet(
+    GetSnippetRequestWrapper request,
+  ) {
+    return _runSafely(
+      () => _defaultClient
+          .getSnippet(_getSnippetRequestToGrpcRequest(request))
+          .then(
+            (response) => GetSnippetResponse(
+              files: _convertToSharedFileList(response.files),
+              sdk: _getAppSdk(response.sdk),
+              pipelineOptions: response.pipelineOptions,
+            ),
+          ),
+    );
+  }
+
+  @override
+  Future<SaveSnippetResponse> saveSnippet(
+    SaveSnippetRequestWrapper request,
+  ) {
+    return _runSafely(
+      () => _defaultClient
+          .saveSnippet(_saveSnippetRequestToGrpcRequest(request))
+          .then(
+            (response) => SaveSnippetResponse(
+              id: response.id,
+            ),
+          ),
+    );
+  }
+
   Future<T> _runSafely<T>(Future<T> Function() invoke) {
     try {
       return invoke();
@@ -186,6 +235,21 @@ class GrpcExampleClient implements ExampleClient {
     return grpc.GetPrecompiledObjectGraphRequest()..cloudPath = request.path;
   }
 
+  grpc.GetSnippetRequest _getSnippetRequestToGrpcRequest(
+    GetSnippetRequestWrapper request,
+  ) {
+    return grpc.GetSnippetRequest()..id = request.id;
+  }
+
+  grpc.SaveSnippetRequest _saveSnippetRequestToGrpcRequest(
+    SaveSnippetRequestWrapper request,
+  ) {
+    return grpc.SaveSnippetRequest()
+      ..sdk = _getGrpcSdk(request.sdk)
+      ..pipelineOptions = request.pipelineOptions
+      ..files.addAll(_convertToSnippetFileList(request.files));
+  }
+
   grpc.Sdk _getGrpcSdk(SDK sdk) {
     switch (sdk) {
       case SDK.java:
@@ -239,7 +303,7 @@ class GrpcExampleClient implements ExampleClient {
       List<CategoryModel> categoriesForSdk = [];
       for (var category in sdkMap.categories) {
         List<ExampleModel> examples = category.precompiledObjects
-            .map((example) => _toExampleModel(example))
+            .map((example) => _toExampleModel(sdk, example))
             .toList()
           ..sort();
         categoriesForSdk.add(CategoryModel(
@@ -253,8 +317,9 @@ class GrpcExampleClient implements ExampleClient {
     return sdkCategoriesMap;
   }
 
-  ExampleModel _toExampleModel(grpc.PrecompiledObject example) {
+  ExampleModel _toExampleModel(SDK sdk, grpc.PrecompiledObject example) {
     return ExampleModel(
+      sdk: sdk,
       name: example.name,
       description: example.description,
       type: _exampleTypeFromString(example.type),
@@ -265,4 +330,37 @@ class GrpcExampleClient implements ExampleClient {
       link: example.link,
     );
   }
+
+  List<SharedFile> _convertToSharedFileList(
+    List<grpc.SnippetFile> snippetFileList,
+  ) {
+    final sharedFilesList = <SharedFile>[];
+
+    for (grpc.SnippetFile item in snippetFileList) {
+      sharedFilesList.add(SharedFile(
+        code: item.content,
+        isMain: item.isMain,
+        name: item.name,
+      ));
+    }
+
+    return sharedFilesList;
+  }
+
+  List<grpc.SnippetFile> _convertToSnippetFileList(
+    List<SharedFile> sharedFilesList,
+  ) {
+    final snippetFileList = <grpc.SnippetFile>[];
+
+    for (SharedFile item in sharedFilesList) {
+      snippetFileList.add(
+        grpc.SnippetFile()
+          ..name = item.name
+          ..isMain = true
+          ..content = item.code,
+      );
+    }
+
+    return snippetFileList;
+  }
 }
diff --git a/playground/frontend/lib/modules/examples/repositories/example_repository.dart b/playground/frontend/lib/modules/examples/repositories/example_repository.dart
index ef4c4e25145..65c09f885fb 100644
--- a/playground/frontend/lib/modules/examples/repositories/example_repository.dart
+++ b/playground/frontend/lib/modules/examples/repositories/example_repository.dart
@@ -19,8 +19,11 @@
 import 'package:playground/modules/examples/models/category_model.dart';
 import 'package:playground/modules/examples/models/example_model.dart';
 import 'package:playground/modules/examples/repositories/example_client/example_client.dart';
+import 'package:playground/modules/examples/repositories/models/get_snippet_request.dart';
+import 'package:playground/modules/examples/repositories/models/get_snippet_response.dart';
 import 'package:playground/modules/examples/repositories/models/get_example_request.dart';
 import 'package:playground/modules/examples/repositories/models/get_list_of_examples_request.dart';
+import 'package:playground/modules/examples/repositories/models/save_snippet_request.dart';
 import 'package:playground/modules/sdk/models/sdk.dart';
 
 class ExampleRepository {
@@ -44,7 +47,9 @@ class ExampleRepository {
     return result.example;
   }
 
-  Future<String> getExampleSource(GetExampleRequestWrapper request) async {
+  Future<String> getExampleSource(
+    GetExampleRequestWrapper request,
+  ) async {
     final result = await _client.getExampleSource(request);
     return result.code;
   }
@@ -76,4 +81,18 @@ class ExampleRepository {
     final result = await _client.getExample(request);
     return result.example;
   }
+
+  Future<GetSnippetResponse> getSnippet(
+    GetSnippetRequestWrapper request,
+  ) async {
+    final result = await _client.getSnippet(request);
+    return result;
+  }
+
+  Future<String> saveSnippet(
+    SaveSnippetRequestWrapper request,
+  ) async {
+    final result = await _client.saveSnippet(request);
+    return result.id;
+  }
 }
diff --git a/playground/frontend/lib/modules/examples/models/selector_size_model.dart b/playground/frontend/lib/modules/examples/repositories/models/get_snippet_request.dart
similarity index 82%
copy from playground/frontend/lib/modules/examples/models/selector_size_model.dart
copy to playground/frontend/lib/modules/examples/repositories/models/get_snippet_request.dart
index 27954f7ae22..4319884981d 100644
--- a/playground/frontend/lib/modules/examples/models/selector_size_model.dart
+++ b/playground/frontend/lib/modules/examples/repositories/models/get_snippet_request.dart
@@ -16,12 +16,10 @@
  * limitations under the License.
  */
 
-class SelectorPositionModel {
-  final double xAlignment;
-  final double yAlignment;
+class GetSnippetRequestWrapper {
+  final String id;
 
-  const SelectorPositionModel({
-    required this.xAlignment,
-    required this.yAlignment,
+  const GetSnippetRequestWrapper({
+    required this.id,
   });
 }
diff --git a/playground/frontend/lib/modules/examples/models/selector_size_model.dart b/playground/frontend/lib/modules/examples/repositories/models/get_snippet_response.dart
similarity index 68%
copy from playground/frontend/lib/modules/examples/models/selector_size_model.dart
copy to playground/frontend/lib/modules/examples/repositories/models/get_snippet_response.dart
index 27954f7ae22..167f725a5fb 100644
--- a/playground/frontend/lib/modules/examples/models/selector_size_model.dart
+++ b/playground/frontend/lib/modules/examples/repositories/models/get_snippet_response.dart
@@ -16,12 +16,17 @@
  * limitations under the License.
  */
 
-class SelectorPositionModel {
-  final double xAlignment;
-  final double yAlignment;
+import 'package:playground/modules/examples/repositories/models/shared_file_model.dart';
+import 'package:playground/modules/sdk/models/sdk.dart';
 
-  const SelectorPositionModel({
-    required this.xAlignment,
-    required this.yAlignment,
+class GetSnippetResponse {
+  final List<SharedFile> files;
+  final SDK sdk;
+  final String pipelineOptions;
+
+  const GetSnippetResponse({
+    required this.files,
+    required this.sdk,
+    required this.pipelineOptions,
   });
 }
diff --git a/playground/frontend/lib/modules/examples/models/selector_size_model.dart b/playground/frontend/lib/modules/examples/repositories/models/save_snippet_request.dart
similarity index 68%
copy from playground/frontend/lib/modules/examples/models/selector_size_model.dart
copy to playground/frontend/lib/modules/examples/repositories/models/save_snippet_request.dart
index 27954f7ae22..c29ee4230f6 100644
--- a/playground/frontend/lib/modules/examples/models/selector_size_model.dart
+++ b/playground/frontend/lib/modules/examples/repositories/models/save_snippet_request.dart
@@ -16,12 +16,17 @@
  * limitations under the License.
  */
 
-class SelectorPositionModel {
-  final double xAlignment;
-  final double yAlignment;
+import 'package:playground/modules/examples/repositories/models/shared_file_model.dart';
+import 'package:playground/modules/sdk/models/sdk.dart';
 
-  const SelectorPositionModel({
-    required this.xAlignment,
-    required this.yAlignment,
+class SaveSnippetRequestWrapper {
+  final List<SharedFile> files;
+  final SDK sdk;
+  final String pipelineOptions;
+
+  const SaveSnippetRequestWrapper({
+    required this.files,
+    required this.sdk,
+    required this.pipelineOptions,
   });
 }
diff --git a/playground/frontend/lib/modules/examples/models/selector_size_model.dart b/playground/frontend/lib/modules/examples/repositories/models/save_snippet_response.dart
similarity index 82%
copy from playground/frontend/lib/modules/examples/models/selector_size_model.dart
copy to playground/frontend/lib/modules/examples/repositories/models/save_snippet_response.dart
index 27954f7ae22..d0379bb73b6 100644
--- a/playground/frontend/lib/modules/examples/models/selector_size_model.dart
+++ b/playground/frontend/lib/modules/examples/repositories/models/save_snippet_response.dart
@@ -16,12 +16,10 @@
  * limitations under the License.
  */
 
-class SelectorPositionModel {
-  final double xAlignment;
-  final double yAlignment;
+class SaveSnippetResponse {
+  final String id;
 
-  const SelectorPositionModel({
-    required this.xAlignment,
-    required this.yAlignment,
+  const SaveSnippetResponse({
+    required this.id,
   });
 }
diff --git a/playground/frontend/lib/modules/examples/models/selector_size_model.dart b/playground/frontend/lib/modules/examples/repositories/models/shared_file_model.dart
similarity index 82%
copy from playground/frontend/lib/modules/examples/models/selector_size_model.dart
copy to playground/frontend/lib/modules/examples/repositories/models/shared_file_model.dart
index 27954f7ae22..7a63f2a91cd 100644
--- a/playground/frontend/lib/modules/examples/models/selector_size_model.dart
+++ b/playground/frontend/lib/modules/examples/repositories/models/shared_file_model.dart
@@ -16,12 +16,14 @@
  * limitations under the License.
  */
 
-class SelectorPositionModel {
-  final double xAlignment;
-  final double yAlignment;
+class SharedFile {
+  final String code;
+  final bool isMain;
+  final String name;
 
-  const SelectorPositionModel({
-    required this.xAlignment,
-    required this.yAlignment,
+  const SharedFile({
+    required this.code,
+    required this.isMain,
+    this.name = '',
   });
 }
diff --git a/playground/frontend/lib/modules/output/components/output.dart b/playground/frontend/lib/modules/output/components/output.dart
index 1f6dac63837..b99692749cb 100644
--- a/playground/frontend/lib/modules/output/components/output.dart
+++ b/playground/frontend/lib/modules/output/components/output.dart
@@ -18,7 +18,9 @@
 
 import 'package:flutter/material.dart';
 import 'package:playground/modules/output/components/output_area.dart';
-import 'package:playground/modules/output/components/output_header/output_header.dart';
+import 'package:playground/modules/output/components/output_header/output_placements.dart';
+import 'package:playground/modules/output/components/output_header/output_tabs.dart';
+import 'package:playground/modules/output/components/output_header/tab_header.dart';
 import 'package:playground/pages/playground/states/playground_state.dart';
 
 const kTabsCount = 2;
@@ -70,10 +72,18 @@ class _OutputState extends State<Output> with SingleTickerProviderStateMixin {
   Widget build(BuildContext context) {
     return Column(
       children: [
-        OutputHeader(
-          tabController: tabController,
-          showOutputPlacements: !widget.isEmbedded,
-          showGraph: widget.showGraph,
+        Row(
+          mainAxisAlignment: MainAxisAlignment.spaceBetween,
+          children: [
+            TabHeader(
+              tabController: tabController,
+              tabsWidget: OutputTabs(
+                tabController: tabController,
+                showGraph: widget.showGraph,
+              ),
+            ),
+            const OutputPlacements(),
+          ],
         ),
         Expanded(
           child: OutputArea(
diff --git a/playground/frontend/lib/modules/output/components/output_header/output_placements.dart b/playground/frontend/lib/modules/output/components/output_header/output_placements.dart
index 09d62198d94..f8eaf571024 100644
--- a/playground/frontend/lib/modules/output/components/output_header/output_placements.dart
+++ b/playground/frontend/lib/modules/output/components/output_header/output_placements.dart
@@ -31,27 +31,30 @@ class OutputPlacements extends StatelessWidget {
   Widget build(BuildContext context) {
     return Consumer<OutputPlacementState>(
       builder: (context, state, child) {
-        return Wrap(
-          spacing: kMdSpacing,
-          children: OutputPlacement.values
-              .map(
-                (placement) => Semantics(
-                  label:
-                      '${AppLocalizations.of(context)!.outputPlacementSemantic}'
-                      ' ${placement.name(context)}',
-                  child: IconButton(
-                    splashRadius: kIconButtonSplashRadius,
-                    icon: SvgPicture.asset(
-                      placement.icon,
-                      color: state.placement == placement
-                          ? Theme.of(context).primaryColor
-                          : null,
+        return Padding(
+          padding: const EdgeInsets.symmetric(horizontal: kXlSpacing),
+          child: Wrap(
+            spacing: kMdSpacing,
+            children: OutputPlacement.values
+                .map(
+                  (placement) => Semantics(
+                    label:
+                        '${AppLocalizations.of(context)!.outputPlacementSemantic}'
+                        ' ${placement.name(context)}',
+                    child: IconButton(
+                      splashRadius: kIconButtonSplashRadius,
+                      icon: SvgPicture.asset(
+                        placement.icon,
+                        color: state.placement == placement
+                            ? Theme.of(context).primaryColor
+                            : null,
+                      ),
+                      onPressed: () => state.setPlacement(placement),
                     ),
-                    onPressed: () => state.setPlacement(placement),
                   ),
-                ),
-              )
-              .toList(),
+                )
+                .toList(),
+          ),
         );
       },
     );
diff --git a/playground/frontend/lib/modules/output/components/output_header/output_header.dart b/playground/frontend/lib/modules/output/components/output_header/tab_header.dart
similarity index 66%
rename from playground/frontend/lib/modules/output/components/output_header/output_header.dart
rename to playground/frontend/lib/modules/output/components/output_header/tab_header.dart
index 95988ee6fcc..dbf60b30f8a 100644
--- a/playground/frontend/lib/modules/output/components/output_header/output_header.dart
+++ b/playground/frontend/lib/modules/output/components/output_header/tab_header.dart
@@ -18,21 +18,18 @@
 
 import 'package:flutter/material.dart';
 import 'package:playground/constants/sizes.dart';
-import 'package:playground/modules/output/components/output_header/output_placements.dart';
 
-import 'output_tabs.dart';
+const kHeaderHeight = 50.0;
 
-class OutputHeader extends StatelessWidget {
+class TabHeader extends StatelessWidget {
   final TabController tabController;
-  final bool showOutputPlacements;
-  final bool showGraph;
+  final Widget tabsWidget;
 
-  const OutputHeader({
-    Key? key,
+  const TabHeader({
+    super.key,
     required this.tabController,
-    this.showOutputPlacements = true,
-    this.showGraph = true,
-  }) : super(key: key);
+    required this.tabsWidget,
+  });
 
   @override
   Widget build(BuildContext context) {
@@ -43,13 +40,7 @@ class OutputHeader extends StatelessWidget {
           horizontal: kXlSpacing,
           vertical: kZeroSpacing,
         ),
-        child: Row(
-          mainAxisAlignment: MainAxisAlignment.spaceBetween,
-          children: [
-            OutputTabs(tabController: tabController, showGraph: showGraph),
-            showOutputPlacements ? const OutputPlacements() : const SizedBox(),
-          ],
-        ),
+        child: tabsWidget,
       ),
     );
   }
diff --git a/playground/frontend/lib/modules/sdk/components/sdk_selector.dart b/playground/frontend/lib/modules/sdk/components/sdk_selector.dart
index 45cacfc5622..4564d0879c9 100644
--- a/playground/frontend/lib/modules/sdk/components/sdk_selector.dart
+++ b/playground/frontend/lib/modules/sdk/components/sdk_selector.dart
@@ -23,7 +23,7 @@ import 'package:playground/constants/sizes.dart';
 import 'package:playground/modules/examples/models/example_model.dart';
 import 'package:playground/modules/sdk/components/sdk_selector_row.dart';
 import 'package:playground/modules/sdk/models/sdk.dart';
-import 'package:playground/pages/playground/states/examples_state.dart';
+import 'package:playground/pages/playground/states/playground_state.dart';
 import 'package:provider/provider.dart';
 
 typedef SetSdk = void Function(SDK sdk);
@@ -62,15 +62,16 @@ class SDKSelector extends StatelessWidget {
             ...SDK.values.map((SDK value) {
               return SizedBox(
                 width: double.infinity,
-                child: Consumer<ExampleState>(
+                child: Consumer<PlaygroundState>(
                   builder: (context, state, child) => SdkSelectorRow(
                     sdk: value,
                     onSelect: () {
                       close();
                       setSdk(value);
                       setExample(
-                        state.defaultExamplesMap[value] ??
+                        state.exampleState.defaultExamplesMap[value] ??
                             ExampleModel(
+                              sdk: value,
                               name: kEmptyExampleName,
                               path: '',
                               description: '',
diff --git a/playground/frontend/lib/modules/sdk/models/sdk.dart b/playground/frontend/lib/modules/sdk/models/sdk.dart
index 17283fcb0b1..bb5eaa2af1b 100644
--- a/playground/frontend/lib/modules/sdk/models/sdk.dart
+++ b/playground/frontend/lib/modules/sdk/models/sdk.dart
@@ -28,6 +28,33 @@ enum SDK {
   go,
   python,
   scio,
+  ;
+
+  /// A temporary solution while we wait for the backend to add
+  /// sdk in example responses.
+  static SDK? tryParseExamplePath(String? path) {
+    if (path == null) {
+      return null;
+    }
+
+    if (path.startsWith('SDK_JAVA')) {
+      return java;
+    }
+
+    if (path.startsWith('SDK_GO')) {
+      return go;
+    }
+
+    if (path.startsWith('SDK_PYTHON')) {
+      return python;
+    }
+
+    if (path.startsWith('SDK_SCIO')) {
+      return scio;
+    }
+
+    return null;
+  }
 }
 
 extension SDKToString on SDK {
diff --git a/playground/frontend/lib/pages/playground/components/editor_textarea_wrapper.dart b/playground/frontend/lib/pages/playground/components/editor_textarea_wrapper.dart
index a5c2115fed0..2a8765b6302 100644
--- a/playground/frontend/lib/pages/playground/components/editor_textarea_wrapper.dart
+++ b/playground/frontend/lib/pages/playground/components/editor_textarea_wrapper.dart
@@ -22,6 +22,7 @@ import 'package:playground/constants/sizes.dart';
 import 'package:playground/modules/analytics/analytics_service.dart';
 import 'package:playground/modules/editor/components/editor_textarea.dart';
 import 'package:playground/modules/editor/components/run_button.dart';
+import 'package:playground/modules/editor/components/share_dropdown/share_button.dart';
 import 'package:playground/modules/examples/components/description_popover/description_popover_button.dart';
 import 'package:playground/modules/examples/components/multifile_popover/multifile_popover_button.dart';
 import 'package:playground/modules/notifications/components/notification.dart';
@@ -79,6 +80,11 @@ class CodeTextAreaWrapper extends StatelessWidget {
                           ),
                         ),
                       ],
+                      Semantics(
+                        container: true,
+                        child: const ShareButton(),
+                      ),
+                      const SizedBox(width: kLgSpacing),
                       Semantics(
                         container: true,
                         child: RunButton(
@@ -92,7 +98,8 @@ class CodeTextAreaWrapper extends StatelessWidget {
                                   (_) => NotificationManager.showError(
                                     context,
                                     AppLocalizations.of(context)!.runCode,
-                                    AppLocalizations.of(context)!.cancelExecution,
+                                    AppLocalizations.of(context)!
+                                        .cancelExecution,
                                   ),
                                 );
                           },
diff --git a/playground/frontend/lib/pages/playground/components/feedback/feedback_dropdown_content.dart b/playground/frontend/lib/pages/playground/components/feedback/feedback_dropdown_content.dart
index d94b5246336..e6694239b06 100644
--- a/playground/frontend/lib/pages/playground/components/feedback/feedback_dropdown_content.dart
+++ b/playground/frontend/lib/pages/playground/components/feedback/feedback_dropdown_content.dart
@@ -17,13 +17,13 @@
  */
 
 import 'package:flutter/material.dart';
+import 'package:playground/components/horizonta_divider/horizontal_divider.dart';
 import 'package:playground/config/theme.dart';
 import 'package:playground/constants/font_weight.dart';
 import 'package:playground/constants/fonts.dart';
 import 'package:playground/constants/sizes.dart';
 import 'package:playground/modules/analytics/analytics_service.dart';
-
-import 'feedback_dropdown_icon_button.dart';
+import 'package:playground/pages/playground/components/feedback/feedback_dropdown_icon_button.dart';
 
 const double kTextFieldWidth = 365.0;
 const double kTextFieldHeight = 68.0;
@@ -124,7 +124,7 @@ class FeedbackDropdownContent extends StatelessWidget {
               ],
             ),
           ),
-          const Divider(height: kDividerHeight),
+          const HorizontalDivider(),
           Padding(
             padding: const EdgeInsets.only(
               top: kXlSpacing,
@@ -171,10 +171,10 @@ class FeedbackDropdownContent extends StatelessWidget {
                     },
                     child: const Text(kSendFeedbackButtonTitle),
                   ),
-                )
+                ),
               ],
             ),
-          )
+          ),
         ],
       ),
     );
diff --git a/playground/frontend/lib/pages/playground/components/playground_page_body.dart b/playground/frontend/lib/pages/playground/components/playground_page_body.dart
index 3b005d2a979..8a723982ed4 100644
--- a/playground/frontend/lib/pages/playground/components/playground_page_body.dart
+++ b/playground/frontend/lib/pages/playground/components/playground_page_body.dart
@@ -70,11 +70,11 @@ class PlaygroundPageBody extends StatelessWidget {
 
   Widget getVerticalSeparator(BuildContext context) => Container(
         width: kMdSpacing,
-        color: ThemeColors.of(context).greyColor,
+        color: ThemeColors.of(context).divider,
       );
 
   Widget getHorizontalSeparator(BuildContext context) => Container(
         height: kMdSpacing,
-        color: ThemeColors.of(context).greyColor,
+        color: ThemeColors.of(context).divider,
       );
 }
diff --git a/playground/frontend/lib/pages/playground/components/playground_page_providers.dart b/playground/frontend/lib/pages/playground/components/playground_page_providers.dart
index beeb95e184b..e61da539b3a 100644
--- a/playground/frontend/lib/pages/playground/components/playground_page_providers.dart
+++ b/playground/frontend/lib/pages/playground/components/playground_page_providers.dart
@@ -17,15 +17,14 @@
  */
 
 import 'package:flutter/material.dart';
-import 'package:playground/constants/params.dart';
 import 'package:playground/modules/analytics/analytics_service.dart';
 import 'package:playground/modules/analytics/google_analytics_service.dart';
 import 'package:playground/modules/editor/repository/code_repository/code_client/grpc_code_client.dart';
 import 'package:playground/modules/editor/repository/code_repository/code_repository.dart';
-import 'package:playground/modules/examples/models/example_model.dart';
 import 'package:playground/modules/examples/repositories/example_client/grpc_example_client.dart';
 import 'package:playground/modules/examples/repositories/example_repository.dart';
 import 'package:playground/modules/output/models/output_placement_state.dart';
+import 'package:playground/pages/playground/states/example_loaders/examples_loader.dart';
 import 'package:playground/pages/playground/states/examples_state.dart';
 import 'package:playground/pages/playground/states/feedback_state.dart';
 import 'package:playground/pages/playground/states/playground_state.dart';
@@ -48,19 +47,12 @@ class PlaygroundPageProviders extends StatelessWidget {
     return MultiProvider(
       providers: [
         Provider<AnalyticsService>(create: (context) => GoogleAnalyticsService()),
-        ChangeNotifierProvider<ExampleState>(
-          create: (context) => ExampleState(kExampleRepository)..init(),
-        ),
-        ChangeNotifierProxyProvider<ExampleState, PlaygroundState>(
-          create: (context) => PlaygroundState(codeRepository: kCodeRepository),
-          update: (context, exampleState, playground) {
-            if (playground == null) {
-              return PlaygroundState(codeRepository: kCodeRepository);
-            }
-
-            _onExampleStateChanged(exampleState, playground);
-            return playground;
-          },
+        ChangeNotifierProvider<PlaygroundState>(
+          create: (context) => PlaygroundState(
+            examplesLoader: ExamplesLoader(),
+            exampleState: ExampleState(kExampleRepository)..init(),
+            codeRepository: kCodeRepository,
+          ),
         ),
         ChangeNotifierProvider<OutputPlacementState>(
           create: (context) => OutputPlacementState(),
@@ -72,110 +64,4 @@ class PlaygroundPageProviders extends StatelessWidget {
       child: child,
     );
   }
-
-  void _onExampleStateChanged(
-    ExampleState exampleState,
-    PlaygroundState playgroundState,
-  ) {
-    // This property currently doubles as a flag of initialization
-    // because it is initialized when an example is ready
-    // and is filled with a null-object if not showing any example.
-    //
-    // TODO: Add a dedicated flag of initialization or make
-    //       PlaygroundState listen for examples and init itself.
-    if (playgroundState.selectedExample != null) {
-      return; // Already initialized.
-    }
-
-    if (_isEmbedded()) {
-      _initEmbedded(exampleState, playgroundState);
-    } else {
-      _initNonEmbedded(exampleState, playgroundState);
-    }
-  }
-
-  bool _isEmbedded() {
-    return Uri.base.toString().contains(kIsEmbedded);
-  }
-
-  Future<void> _initEmbedded(
-    ExampleState exampleState,
-    PlaygroundState playgroundState,
-  ) async {
-    final example = _getEmbeddedExample();
-
-    if (example.path.isEmpty) {
-      String source = Uri.base.queryParameters[kSourceCode] ?? '';
-      example.setSource(source);
-      playgroundState.setExample(example);
-    } else {
-      final loadedExample = await exampleState.getExample(
-        example.path,
-        playgroundState.sdk,
-      );
-
-      final exampleWithInfo = await exampleState.loadExampleInfo(
-        loadedExample,
-        playgroundState.sdk,
-      );
-
-      playgroundState.setExample(exampleWithInfo);
-    }
-  }
-
-  ExampleModel _getEmbeddedExample() {
-    final examplePath = Uri.base.queryParameters[kExampleParam];
-
-    return ExampleModel(
-      name: 'Embedded_Example',
-      path: examplePath ?? '',
-      description: '',
-      type: ExampleType.example,
-    );
-  }
-
-  Future<void> _initNonEmbedded(
-    ExampleState exampleState,
-    PlaygroundState playgroundState,
-  ) async {
-    await exampleState.loadDefaultExamplesIfNot();
-
-    final example = await _getExample(exampleState, playgroundState);
-
-    if (example == null) {
-      return;
-    }
-
-    final exampleWithInfo = await exampleState.loadExampleInfo(
-      example,
-      playgroundState.sdk,
-    );
-
-    playgroundState.setExample(exampleWithInfo);
-  }
-
-  Future<ExampleModel?> _getExample(
-    ExampleState exampleState,
-    PlaygroundState playground,
-  ) async {
-    final examplePath = Uri.base.queryParameters[kExampleParam];
-
-    if (examplePath?.isEmpty ?? true) {
-      return exampleState.defaultExamplesMap[playground.sdk];
-    }
-
-    final allExamples = exampleState.sdkCategories?.values
-        .expand((sdkCategory) => sdkCategory.map((e) => e.examples))
-        .expand((element) => element)
-        .toList();
-
-    if (allExamples?.isEmpty ?? true) {
-      return null;
-    }
-
-    return allExamples?.firstWhere(
-      (example) => example.path == examplePath,
-      orElse: () => exampleState.defaultExample!,
-    );
-  }
 }
diff --git a/playground/frontend/lib/pages/playground/playground_page.dart b/playground/frontend/lib/pages/playground/playground_page.dart
index 75725c2d59b..ee4029cbda6 100644
--- a/playground/frontend/lib/pages/playground/playground_page.dart
+++ b/playground/frontend/lib/pages/playground/playground_page.dart
@@ -33,7 +33,6 @@ import 'package:playground/pages/playground/components/close_listener_nonweb.dar
 import 'package:playground/pages/playground/components/more_actions.dart';
 import 'package:playground/pages/playground/components/playground_page_body.dart';
 import 'package:playground/pages/playground/components/playground_page_footer.dart';
-import 'package:playground/pages/playground/states/examples_state.dart';
 import 'package:playground/pages/playground/states/playground_state.dart';
 import 'package:provider/provider.dart';
 
@@ -55,14 +54,10 @@ class PlaygroundPage extends StatelessWidget {
                   spacing: kXlSpacing,
                   children: [
                     const Logo(),
-                    Consumer<ExampleState>(
-                      builder: (context, state, child) {
-                        return ExampleSelector(
-                          changeSelectorVisibility:
-                              state.changeSelectorVisibility,
-                          isSelectorOpened: state.isSelectorOpened,
-                        );
-                      },
+                    ExampleSelector(
+                      changeSelectorVisibility:
+                          state.exampleState.changeSelectorVisibility,
+                      isSelectorOpened: state.exampleState.isSelectorOpened,
                     ),
                     SDKSelector(
                       sdk: state.sdk,
diff --git a/playground/frontend/lib/pages/playground/states/example_loaders/catalog_default_example_loader.dart b/playground/frontend/lib/pages/playground/states/example_loaders/catalog_default_example_loader.dart
new file mode 100644
index 00000000000..e07d4cb67d6
--- /dev/null
+++ b/playground/frontend/lib/pages/playground/states/example_loaders/catalog_default_example_loader.dart
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import 'package:playground/modules/examples/models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_model.dart';
+import 'package:playground/pages/playground/states/example_loaders/example_loader.dart';
+import 'package:playground/pages/playground/states/examples_state.dart';
+
+class CatalogDefaultExampleLoader extends ExampleLoader {
+  final CatalogDefaultExampleLoadingDescriptor descriptor;
+  final ExampleState exampleState;
+
+  const CatalogDefaultExampleLoader({
+    required this.descriptor,
+    required this.exampleState,
+  });
+
+  @override
+  Future<ExampleModel> get future async {
+    if (!exampleState.hasExampleCatalog) {
+      throw Exception('Default example requires a catalog in ExampleState');
+    }
+
+    await exampleState.loadDefaultExamplesIfNot();
+    final result = exampleState.defaultExamplesMap[descriptor.sdk];
+
+    if (result == null) {
+      throw Exception('Default example not found for $descriptor');
+    }
+
+    return result;
+  }
+}
diff --git a/playground/frontend/test/pages/playground/states/mocks/example_mock.dart b/playground/frontend/lib/pages/playground/states/example_loaders/empty_example_loader.dart
similarity index 52%
copy from playground/frontend/test/pages/playground/states/mocks/example_mock.dart
copy to playground/frontend/lib/pages/playground/states/example_loaders/empty_example_loader.dart
index 0be09e915f0..5552d31fb45 100644
--- a/playground/frontend/test/pages/playground/states/mocks/example_mock.dart
+++ b/playground/frontend/lib/pages/playground/states/example_loaders/empty_example_loader.dart
@@ -16,38 +16,26 @@
  * limitations under the License.
  */
 
+import 'package:playground/modules/examples/models/example_loading_descriptors/empty_example_loading_descriptor.dart';
 import 'package:playground/modules/examples/models/example_model.dart';
+import 'package:playground/pages/playground/states/example_loaders/example_loader.dart';
+import 'package:playground/pages/playground/states/examples_state.dart';
 
-final ExampleModel exampleMock1 = ExampleModel(
-  source: 'ex1',
-  name: 'Example',
-  type: ExampleType.example,
-  description: 'description',
-  path: 'SDK/Category/Name',
-);
+class EmptyExampleLoader extends ExampleLoader {
+  final EmptyExampleLoadingDescriptor descriptor;
+  final ExampleState exampleState;
 
-final ExampleModel exampleMock2 = ExampleModel(
-  source: 'ex2',
-  name: 'Kata',
-  type: ExampleType.kata,
-  description: 'description',
-  path: 'SDK/Category/Name',
-);
+  const EmptyExampleLoader({
+    required this.descriptor,
+    required this.exampleState,
+  });
 
-final ExampleModel exampleWithoutSourceMock = ExampleModel(
-  name: 'Test example',
-  type: ExampleType.example,
-  description: 'description',
-  path: 'SDK/Category/Name',
-);
-
-final ExampleModel exampleWithAllAdditionsMock = ExampleModel(
-  name: 'Test example',
-  type: ExampleType.example,
-  description: 'description',
-  path: 'SDK/Category/Name',
-  source: 'test outputs',
-  outputs: 'test outputs',
-  logs: 'test outputs',
-  graph: 'test outputs',
-);
+  @override
+  Future<ExampleModel> get future async => ExampleModel(
+        sdk: descriptor.sdk,
+        name: 'Embedded_Example',
+        path: '',
+        description: '',
+        type: ExampleType.example,
+      );
+}
diff --git a/playground/frontend/lib/modules/examples/models/selector_size_model.dart b/playground/frontend/lib/pages/playground/states/example_loaders/example_loader.dart
similarity index 81%
copy from playground/frontend/lib/modules/examples/models/selector_size_model.dart
copy to playground/frontend/lib/pages/playground/states/example_loaders/example_loader.dart
index 27954f7ae22..9c6b256683c 100644
--- a/playground/frontend/lib/modules/examples/models/selector_size_model.dart
+++ b/playground/frontend/lib/pages/playground/states/example_loaders/example_loader.dart
@@ -16,12 +16,10 @@
  * limitations under the License.
  */
 
-class SelectorPositionModel {
-  final double xAlignment;
-  final double yAlignment;
+import 'package:playground/modules/examples/models/example_model.dart';
 
-  const SelectorPositionModel({
-    required this.xAlignment,
-    required this.yAlignment,
-  });
+abstract class ExampleLoader {
+  const ExampleLoader();
+
+  Future<ExampleModel> get future;
 }
diff --git a/playground/frontend/lib/pages/playground/states/example_loaders/examples_loader.dart b/playground/frontend/lib/pages/playground/states/example_loaders/examples_loader.dart
new file mode 100644
index 00000000000..f9ae8a7058b
--- /dev/null
+++ b/playground/frontend/lib/pages/playground/states/example_loaders/examples_loader.dart
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import 'package:playground/modules/examples/models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_loading_descriptors/empty_example_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_loading_descriptors/standard_example_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_loading_descriptors/user_shared_example_loading_descriptor.dart';
+import 'package:playground/pages/playground/states/example_loaders/catalog_default_example_loader.dart';
+import 'package:playground/pages/playground/states/example_loaders/empty_example_loader.dart';
+import 'package:playground/pages/playground/states/example_loaders/example_loader.dart';
+import 'package:playground/pages/playground/states/example_loaders/standard_example_loader.dart';
+import 'package:playground/pages/playground/states/example_loaders/user_shared_example_loader.dart';
+import 'package:playground/pages/playground/states/playground_state.dart';
+
+class ExamplesLoader {
+  PlaygroundState? _playgroundState;
+  ExamplesLoadingDescriptor? _descriptor;
+
+  void setPlaygroundState(PlaygroundState value) {
+    _playgroundState = value;
+  }
+
+  Future<void> load(ExamplesLoadingDescriptor descriptor) async {
+    if (_descriptor == descriptor) {
+      return;
+    }
+
+    _descriptor = descriptor;
+    await Future.wait(descriptor.descriptors.map(_loadOne));
+  }
+
+  Future<void> _loadOne(ExampleLoadingDescriptor descriptor) async {
+    final example = await _getOneLoader(descriptor).future;
+    _playgroundState!.setExample(example);
+  }
+
+  ExampleLoader _getOneLoader(ExampleLoadingDescriptor descriptor) {
+    final exampleState = _playgroundState!.exampleState;
+
+    if (descriptor is CatalogDefaultExampleLoadingDescriptor) {
+      return CatalogDefaultExampleLoader(
+        descriptor: descriptor,
+        exampleState: exampleState,
+      );
+    }
+
+    if (descriptor is EmptyExampleLoadingDescriptor) {
+      return EmptyExampleLoader(
+        descriptor: descriptor,
+        exampleState: exampleState,
+      );
+    }
+
+    if (descriptor is StandardExampleLoadingDescriptor) {
+      return StandardExampleLoader(
+        descriptor: descriptor,
+        exampleState: exampleState,
+      );
+    }
+
+    if (descriptor is UserSharedExampleLoadingDescriptor) {
+      return UserSharedExampleLoader(
+        descriptor: descriptor,
+        exampleState: exampleState,
+      );
+    }
+
+    throw Exception('Unknown example loading descriptor: $descriptor');
+  }
+}
diff --git a/playground/frontend/lib/pages/playground/states/example_loaders/standard_example_loader.dart b/playground/frontend/lib/pages/playground/states/example_loaders/standard_example_loader.dart
new file mode 100644
index 00000000000..667f8f24e1d
--- /dev/null
+++ b/playground/frontend/lib/pages/playground/states/example_loaders/standard_example_loader.dart
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import 'dart:async';
+
+import 'package:playground/modules/examples/models/example_loading_descriptors/standard_example_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_model.dart';
+import 'package:playground/modules/sdk/models/sdk.dart';
+import 'package:playground/pages/playground/states/example_loaders/example_loader.dart';
+import 'package:playground/pages/playground/states/examples_state.dart';
+
+/// Loads a given example from the local cache, then adds info from network.
+///
+/// This loader assumes that [ExampleState] is loading all examples to
+/// its cache. So it only completes if this is successful.
+class StandardExampleLoader extends ExampleLoader {
+  final StandardExampleLoadingDescriptor descriptor;
+  final ExampleState exampleState;
+  final _completer = Completer<ExampleModel>();
+
+  @override
+  Future<ExampleModel> get future => _completer.future;
+
+  StandardExampleLoader({
+    required this.descriptor,
+    required this.exampleState,
+  }) {
+    _load();
+  }
+
+  void _load() async {
+    final example = await _loadExampleWithoutInfo();
+
+    if (example == null) {
+      _completer.completeError('Example not found: $descriptor');
+      return;
+    }
+
+    _completer.complete(
+      exampleState.loadExampleInfo(example),
+    );
+  }
+
+  Future<ExampleModel?> _loadExampleWithoutInfo() {
+    return exampleState.hasExampleCatalog
+        ? exampleState.getCatalogExampleByPath(descriptor.path)
+        : _loadExampleFromRepository();
+  }
+
+  Future<ExampleModel?> _loadExampleFromRepository() async {
+    final sdk = SDK.tryParseExamplePath(descriptor.path);
+
+    if (sdk == null) {
+      return null;
+    }
+
+    return exampleState.getExample(descriptor.path, sdk);
+  }
+}
diff --git a/playground/frontend/lib/modules/examples/models/selector_size_model.dart b/playground/frontend/lib/pages/playground/states/example_loaders/user_shared_example_loader.dart
similarity index 53%
rename from playground/frontend/lib/modules/examples/models/selector_size_model.dart
rename to playground/frontend/lib/pages/playground/states/example_loaders/user_shared_example_loader.dart
index 27954f7ae22..0a658718a0b 100644
--- a/playground/frontend/lib/modules/examples/models/selector_size_model.dart
+++ b/playground/frontend/lib/pages/playground/states/example_loaders/user_shared_example_loader.dart
@@ -16,12 +16,21 @@
  * limitations under the License.
  */
 
-class SelectorPositionModel {
-  final double xAlignment;
-  final double yAlignment;
+import 'package:playground/modules/examples/models/example_loading_descriptors/user_shared_example_loading_descriptor.dart';
+import 'package:playground/modules/examples/models/example_model.dart';
+import 'package:playground/pages/playground/states/example_loaders/example_loader.dart';
+import 'package:playground/pages/playground/states/examples_state.dart';
 
-  const SelectorPositionModel({
-    required this.xAlignment,
-    required this.yAlignment,
+class UserSharedExampleLoader extends ExampleLoader {
+  final UserSharedExampleLoadingDescriptor descriptor;
+  final ExampleState exampleState;
+
+  UserSharedExampleLoader({
+    required this.descriptor,
+    required this.exampleState,
   });
+
+  @override
+  Future<ExampleModel> get future =>
+      exampleState.loadSharedExample(descriptor.snippetId);
 }
diff --git a/playground/frontend/lib/pages/playground/states/example_selector_state.dart b/playground/frontend/lib/pages/playground/states/example_selector_state.dart
index 4e034535f36..fd9f8a1f907 100644
--- a/playground/frontend/lib/pages/playground/states/example_selector_state.dart
+++ b/playground/frontend/lib/pages/playground/states/example_selector_state.dart
@@ -21,17 +21,13 @@ import 'package:playground/modules/examples/models/category_model.dart';
 import 'package:playground/modules/examples/models/example_model.dart';
 import 'package:playground/pages/playground/states/playground_state.dart';
 
-import 'examples_state.dart';
-
 class ExampleSelectorState with ChangeNotifier {
-  final ExampleState _exampleState;
   final PlaygroundState _playgroundState;
   ExampleType _selectedFilterType;
   String _filterText;
   List<CategoryModel> categories;
 
   ExampleSelectorState(
-    this._exampleState,
     this._playgroundState,
     this.categories, [
     this._selectedFilterType = ExampleType.all,
@@ -42,23 +38,26 @@ class ExampleSelectorState with ChangeNotifier {
 
   String get filterText => _filterText;
 
-  setSelectedFilterType(ExampleType type) {
+  void setSelectedFilterType(ExampleType type) {
     _selectedFilterType = type;
     notifyListeners();
   }
 
-  setFilterText(String text) {
+  void setFilterText(String text) {
     _filterText = text;
     notifyListeners();
   }
 
-  setCategories(List<CategoryModel>? categories) {
+  void setCategories(List<CategoryModel>? categories) {
     this.categories = categories ?? [];
     notifyListeners();
   }
 
-  sortCategories() {
-    final categories = _exampleState.getCategories(_playgroundState.sdk)!;
+  void sortCategories() {
+    final categories = _playgroundState.exampleState.getCategories(
+      _playgroundState.sdk,
+    )!;
+
     final sortedCategories = categories
         .map((category) => CategoryModel(
             name: category.name,
diff --git a/playground/frontend/lib/pages/playground/states/examples_state.dart b/playground/frontend/lib/pages/playground/states/examples_state.dart
index b080e361566..ff307dba928 100644
--- a/playground/frontend/lib/pages/playground/states/examples_state.dart
+++ b/playground/frontend/lib/pages/playground/states/examples_state.dart
@@ -16,13 +16,20 @@
  * limitations under the License.
  */
 
+import 'dart:async';
+
+import 'package:collection/collection.dart';
 import 'package:flutter/material.dart';
 import 'package:playground/constants/params.dart';
 import 'package:playground/modules/examples/models/category_model.dart';
 import 'package:playground/modules/examples/models/example_model.dart';
 import 'package:playground/modules/examples/repositories/example_repository.dart';
+import 'package:playground/modules/examples/repositories/models/get_snippet_request.dart';
+import 'package:playground/modules/examples/repositories/models/get_snippet_response.dart';
 import 'package:playground/modules/examples/repositories/models/get_example_request.dart';
 import 'package:playground/modules/examples/repositories/models/get_list_of_examples_request.dart';
+import 'package:playground/modules/examples/repositories/models/save_snippet_request.dart';
+import 'package:playground/modules/examples/repositories/models/shared_file_model.dart';
 import 'package:playground/modules/sdk/models/sdk.dart';
 
 class ExampleState with ChangeNotifier {
@@ -32,17 +39,26 @@ class ExampleState with ChangeNotifier {
   ExampleModel? defaultExample;
   bool isSelectorOpened = false;
 
+  final _allExamplesCompleter = Completer<void>();
+
+  Future<void> get allExamplesFuture => _allExamplesCompleter.future;
+
+  bool get hasExampleCatalog => !isEmbedded();
+
   ExampleState(this._exampleRepository);
 
-  init() {
-    if (!Uri.base.toString().contains(kIsEmbedded)) {
-      _loadCategories();
+  Future<void> init() async {
+    if (hasExampleCatalog) {
+      await Future.wait([
+        _loadCategories(),
+        loadDefaultExamplesIfNot(),
+      ]);
     }
   }
 
-  setSdkCategories(Map<SDK, List<CategoryModel>> map) {
+  void setSdkCategories(Map<SDK, List<CategoryModel>> map) {
     sdkCategories = map;
-    notifyListeners();
+    _allExamplesCompleter.complete();
   }
 
   List<CategoryModel>? getCategories(SDK sdk) {
@@ -79,18 +95,46 @@ class ExampleState with ChangeNotifier {
     );
   }
 
-  Future<ExampleModel> loadExampleInfo(ExampleModel example, SDK sdk) async {
+  Future<ExampleModel> loadSharedExample(String id) async {
+    GetSnippetResponse result = await _exampleRepository.getSnippet(
+      GetSnippetRequestWrapper(id: id),
+    );
+    return ExampleModel(
+      sdk: result.sdk,
+      name: result.files.first.name,
+      path: id,
+      description: '',
+      type: ExampleType.example,
+      source: result.files.first.code,
+      pipelineOptions: result.pipelineOptions,
+    );
+  }
+
+  Future<String> getSnippetId({
+    required List<SharedFile> files,
+    required SDK sdk,
+    required String pipelineOptions,
+  }) async {
+    String id = await _exampleRepository.saveSnippet(SaveSnippetRequestWrapper(
+      files: files,
+      sdk: sdk,
+      pipelineOptions: pipelineOptions,
+    ));
+    return id;
+  }
+
+  Future<ExampleModel> loadExampleInfo(ExampleModel example) async {
     if (example.isInfoFetched()) {
       return example;
     }
 
     //GRPC GetPrecompiledGraph errors hotfix
     if (example.name == 'MinimalWordCount' &&
-        (sdk == SDK.go || sdk == SDK.scio)) {
+        (example.sdk == SDK.go || example.sdk == SDK.scio)) {
       final exampleData = await Future.wait([
-        getExampleSource(example.path, sdk),
-        getExampleOutput(example.path, sdk),
-        getExampleLogs(example.path, sdk),
+        getExampleSource(example.path, example.sdk),
+        getExampleOutput(example.path, example.sdk),
+        getExampleLogs(example.path, example.sdk),
       ]);
       example.setSource(exampleData[0]);
       example.setOutputs(exampleData[1]);
@@ -99,10 +143,10 @@ class ExampleState with ChangeNotifier {
     }
 
     final exampleData = await Future.wait([
-      getExampleSource(example.path, sdk),
-      getExampleOutput(example.path, sdk),
-      getExampleLogs(example.path, sdk),
-      getExampleGraph(example.path, sdk)
+      getExampleSource(example.path, example.sdk),
+      getExampleOutput(example.path, example.sdk),
+      getExampleLogs(example.path, example.sdk),
+      getExampleGraph(example.path, example.sdk)
     ]);
     example.setSource(exampleData[0]);
     example.setOutputs(exampleData[1]);
@@ -111,15 +155,15 @@ class ExampleState with ChangeNotifier {
     return example;
   }
 
-  _loadCategories() {
-    _exampleRepository
+  Future<void> _loadCategories() {
+    return _exampleRepository
         .getListOfExamples(
           GetListOfExamplesRequestWrapper(sdk: null, category: null),
         )
         .then((map) => setSdkCategories(map));
   }
 
-  changeSelectorVisibility() {
+  void changeSelectorVisibility() {
     isSelectorOpened = !isSelectorOpened;
     notifyListeners();
   }
@@ -147,7 +191,7 @@ class ExampleState with ChangeNotifier {
     final futures = <Future<void>>[];
 
     for (var entry in defaultExamplesMap.entries) {
-      final exampleFuture = loadExampleInfo(entry.value, entry.key)
+      final exampleFuture = loadExampleInfo(entry.value)
           .then((value) => defaultExamplesMap[entry.key] = value);
       futures.add(exampleFuture);
     }
@@ -163,4 +207,16 @@ class ExampleState with ChangeNotifier {
 
     await loadDefaultExamples();
   }
+
+  Future<ExampleModel?> getCatalogExampleByPath(String path) async {
+    await allExamplesFuture;
+
+    final allExamples = sdkCategories?.values
+        .expand((sdkCategory) => sdkCategory.map((e) => e.examples))
+        .expand((element) => element);
+
+    return allExamples?.firstWhereOrNull(
+      (e) => e.path == path,
+    );
+  }
 }
diff --git a/playground/frontend/lib/pages/playground/states/playground_state.dart b/playground/frontend/lib/pages/playground/states/playground_state.dart
index 2340de7b8f8..c0173e47153 100644
--- a/playground/frontend/lib/pages/playground/states/playground_state.dart
+++ b/playground/frontend/lib/pages/playground/states/playground_state.dart
@@ -25,9 +25,12 @@ import 'package:playground/modules/editor/parsers/run_options_parser.dart';
 import 'package:playground/modules/editor/repository/code_repository/code_repository.dart';
 import 'package:playground/modules/editor/repository/code_repository/run_code_request.dart';
 import 'package:playground/modules/editor/repository/code_repository/run_code_result.dart';
+import 'package:playground/modules/examples/models/example_loading_descriptors/examples_loading_descriptor_factory.dart';
 import 'package:playground/modules/examples/models/example_model.dart';
 import 'package:playground/modules/examples/models/outputs_model.dart';
 import 'package:playground/modules/sdk/models/sdk.dart';
+import 'package:playground/pages/playground/states/example_loaders/examples_loader.dart';
+import 'package:playground/pages/playground/states/examples_state.dart';
 
 const kTitleLength = 15;
 const kExecutionTimeUpdate = 100;
@@ -40,6 +43,9 @@ const kCachedResultsLog =
     'The results of this example are taken from the Apache Beam Playground cache.\n';
 
 class PlaygroundState with ChangeNotifier {
+  final ExampleState exampleState;
+  final ExamplesLoader examplesLoader;
+
   final CodeController codeController;
   SDK _sdk;
   CodeRepository? _codeRepository;
@@ -52,17 +58,24 @@ class PlaygroundState with ChangeNotifier {
   String? outputResult;
 
   PlaygroundState({
+    required this.exampleState,
+    required this.examplesLoader,
     SDK sdk = SDK.java,
-    ExampleModel? selectedExample,
     CodeRepository? codeRepository,
   })  : _sdk = sdk,
         codeController = CodeController(
           language: sdk.highlightMode,
           webSpaceFix: false,
         ) {
-    _selectedExample = selectedExample;
-    _pipelineOptions = selectedExample?.pipelineOptions ?? '';
-    codeController.text = _selectedExample?.source ?? '';
+    final uri = Uri.base;
+    final descriptor = ExamplesLoadingDescriptorFactory.fromUriParts(
+      path: uri.path,
+      params: uri.queryParameters,
+    );
+
+    examplesLoader.setPlaygroundState(this);
+    examplesLoader.load(descriptor);
+
     _codeRepository = codeRepository;
     selectedOutputFilterType = OutputType.all;
     outputResult = '';
@@ -101,6 +114,7 @@ class PlaygroundState with ChangeNotifier {
 
   void setExample(ExampleModel example) {
     _selectedExample = example;
+    setSdk(example.sdk, notify: false);
     _pipelineOptions = example.pipelineOptions ?? '';
     codeController.text = example.source ?? '';
     _result = null;
@@ -109,13 +123,16 @@ class PlaygroundState with ChangeNotifier {
     notifyListeners();
   }
 
-  void setSdk(SDK sdk) {
+  void setSdk(SDK sdk, {bool notify = true}) {
     _sdk = sdk;
     codeController.language = sdk.highlightMode;
-    notifyListeners();
+
+    if (notify) {
+      notifyListeners();
+    }
   }
 
-  void setSource(String source) {
+  set source(String source) {
     codeController.text = source;
   }
 
@@ -138,7 +155,7 @@ class PlaygroundState with ChangeNotifier {
     codeController.text = _selectedExample?.source ?? '';
     _pipelineOptions = selectedExample?.pipelineOptions ?? '';
     _executionTime = null;
-    setOutputResult('');
+    outputResult = '';
     notifyListeners();
   }
 
diff --git a/playground/frontend/lib/playground_app.dart b/playground/frontend/lib/playground_app.dart
index 4fbde3ff576..8c5f2a489e6 100644
--- a/playground/frontend/lib/playground_app.dart
+++ b/playground/frontend/lib/playground_app.dart
@@ -23,7 +23,6 @@ import 'package:flutter_localizations/flutter_localizations.dart';
 import 'package:playground/config/locale.dart';
 import 'package:playground/config/theme.dart';
 import 'package:playground/l10n/l10n.dart';
-import 'package:playground/modules/editor/components/editor_themes.dart';
 import 'package:playground/pages/playground/components/playground_page_providers.dart';
 import 'package:playground/pages/playground/playground_page.dart';
 import 'package:playground/pages/routes.dart';
@@ -34,38 +33,38 @@ class PlaygroundApp extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return ChangeNotifierProvider(
-      create: (context) => ThemeProvider()..init(),
-      builder: (context, _) {
-        final themeProvider = Provider.of<ThemeProvider>(context);
-        return CodeTheme(
-          data: themeProvider.isDarkMode ? kDarkCodeTheme : kLightCodeTheme,
-          child: ChangeNotifierProvider<LocaleProvider>(
-            create: (context) => LocaleProvider(),
-            builder: (context, state) {
-              final localeProvider = Provider.of<LocaleProvider>(context);
-              return PlaygroundPageProviders(
-                child: MaterialApp(
-                  title: 'Apache Beam Playground',
-                  themeMode: themeProvider.themeMode,
-                  theme: kLightTheme,
-                  darkTheme: kDarkTheme,
-                  onGenerateRoute: Routes.generateRoute,
-                  home: const PlaygroundPage(),
-                  debugShowCheckedModeBanner: false,
-                  locale: localeProvider.locale,
-                  supportedLocales: L10n.locales,
-                  localizationsDelegates: const [
-                    AppLocalizations.delegate,
-                    GlobalMaterialLocalizations.delegate,
-                    GlobalWidgetsLocalizations.delegate,
-                  ],
-                ),
-              );
-            },
-          ),
-        );
-      },
+    return ThemeSwitchNotifierProvider(
+      child: Consumer<ThemeSwitchNotifier>(
+        builder: (context, themeSwitchNotifier, _) {
+          return CodeTheme(
+            data: themeSwitchNotifier.codeTheme,
+            child: ChangeNotifierProvider<LocaleProvider>(
+              create: (context) => LocaleProvider(),
+              builder: (context, state) {
+                final localeProvider = Provider.of<LocaleProvider>(context);
+                return PlaygroundPageProviders(
+                  child: MaterialApp(
+                    title: 'Apache Beam Playground',
+                    themeMode: themeSwitchNotifier.themeMode,
+                    theme: kLightTheme,
+                    darkTheme: kDarkTheme,
+                    onGenerateRoute: Routes.generateRoute,
+                    home: const PlaygroundPage(),
+                    debugShowCheckedModeBanner: false,
+                    locale: localeProvider.locale,
+                    supportedLocales: L10n.locales,
+                    localizationsDelegates: const [
+                      AppLocalizations.delegate,
+                      GlobalMaterialLocalizations.delegate,
+                      GlobalWidgetsLocalizations.delegate,
+                    ],
+                  ),
+                );
+              },
+            ),
+          );
+        },
+      ),
     );
   }
 }
diff --git a/playground/frontend/lib/utils/dropdown_utils.dart b/playground/frontend/lib/utils/dropdown_utils.dart
new file mode 100644
index 00000000000..6ebb251a3df
--- /dev/null
+++ b/playground/frontend/lib/utils/dropdown_utils.dart
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import 'package:flutter/material.dart';
+import 'package:playground/components/dropdown_button/dropdown_button.dart';
+
+const _bottomToDropdown = 10.0;
+
+/// Returns the screen offset at which to show the dropdown.
+///
+/// [key] points to the button that triggers the dropdown.
+/// [widgetWidth] is important when aligning to the right.
+Offset findDropdownOffset({
+  required GlobalKey key,
+  DropdownAlignment alignment = DropdownAlignment.left,
+  double widgetWidth = 0,
+}) {
+  final box = key.currentContext?.findRenderObject() as RenderBox?;
+
+  if (box == null) {
+    throw Exception('Cannot find render object for $key');
+  }
+
+  final buttonOffset = box.localToGlobal(Offset.zero);
+  final top = buttonOffset.dy + box.size.height + _bottomToDropdown;
+
+  switch (alignment) {
+    case DropdownAlignment.left:
+      return Offset(buttonOffset.dx, top);
+    case DropdownAlignment.right:
+      return Offset(buttonOffset.dx + box.size.width - widgetWidth, top);
+  }
+}
diff --git a/playground/frontend/lib/utils/share_code_utils.dart b/playground/frontend/lib/utils/share_code_utils.dart
new file mode 100644
index 00000000000..03aac0821b3
--- /dev/null
+++ b/playground/frontend/lib/utils/share_code_utils.dart
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import 'dart:convert';
+
+import 'package:playground/constants/params.dart';
+
+enum PlaygroundView {
+  standalone,
+  embedded,
+  ;
+
+  String get path {
+    switch (this) {
+      case PlaygroundView.standalone:
+        return '/';
+      case PlaygroundView.embedded:
+        return '/embedded';
+    }
+  }
+}
+
+extension CopyWith on Uri {
+  Uri copyWith({
+    String? path,
+    Map<String, dynamic>? queryParameters,
+  }) {
+    return Uri(
+      scheme: scheme,
+      userInfo: userInfo,
+      host: host,
+      port: port,
+      path: path ?? this.path,
+      queryParameters: queryParameters ?? this.queryParameters,
+    );
+  }
+}
+
+class ShareCodeUtils {
+  static const _width = '90%';
+  static const _height = '600px';
+
+  static Uri examplePathToPlaygroundUrl({
+    required String examplePath,
+    required PlaygroundView view,
+  }) {
+    return Uri.base.copyWith(
+      path: view.path,
+      queryParameters: _getExampleQueryParameters(
+        examplePath: examplePath,
+        view: view,
+      ),
+    );
+  }
+
+  static Map<String, dynamic> _getExampleQueryParameters({
+    required String examplePath,
+    required PlaygroundView view,
+  }) {
+    switch (view) {
+      case PlaygroundView.standalone:
+        return {
+          kExampleParam: examplePath,
+        };
+      case PlaygroundView.embedded:
+        return {
+          kIsEditableParam: '1',
+          kExampleParam: examplePath,
+        };
+    }
+  }
+
+  static String examplePathToIframeCode({
+    required String examplePath,
+  }) {
+    return _iframe(
+      src: examplePathToPlaygroundUrl(
+        examplePath: examplePath,
+        view: PlaygroundView.embedded,
+      ),
+    );
+  }
+
+  static String _iframe({
+    required Uri src,
+  }) {
+    return '<iframe'
+        ' src="${Uri.encodeComponent(src.toString())}"'
+        ' width="${_htmlEscape(_width)}"'
+        ' height="${_htmlEscape(_height)}"'
+        ' allow="clipboard-write" '
+        '></iframe>';
+  }
+
+  static String _htmlEscape(String text) => const HtmlEscape().convert(text);
+}
diff --git a/playground/frontend/test/modules/editor/repository/example_repository/example_repository_test.mocks.dart b/playground/frontend/test/modules/editor/repository/example_repository/example_repository_test.mocks.dart
index 61dd8b27670..92bcb2546a5 100644
--- a/playground/frontend/test/modules/editor/repository/example_repository/example_repository_test.mocks.dart
+++ b/playground/frontend/test/modules/editor/repository/example_repository/example_repository_test.mocks.dart
@@ -20,23 +20,31 @@
 // in playground/test/modules/editor/repository/example_repository/example_repository_test.dart.
 // Do not manually edit this file.
 
-import 'dart:async' as _i7;
+import 'dart:async' as _i9;
 
 import 'package:mockito/mockito.dart' as _i1;
 import 'package:playground/modules/editor/repository/code_repository/code_client/output_response.dart'
     as _i5;
 import 'package:playground/modules/examples/repositories/example_client/example_client.dart'
-    as _i6;
+    as _i8;
 import 'package:playground/modules/examples/repositories/models/get_example_code_response.dart'
     as _i3;
 import 'package:playground/modules/examples/repositories/models/get_example_request.dart'
-    as _i9;
+    as _i11;
 import 'package:playground/modules/examples/repositories/models/get_example_response.dart'
     as _i4;
 import 'package:playground/modules/examples/repositories/models/get_list_of_examples_request.dart'
-    as _i8;
+    as _i10;
 import 'package:playground/modules/examples/repositories/models/get_list_of_examples_response.dart'
     as _i2;
+import 'package:playground/modules/examples/repositories/models/get_snippet_request.dart'
+    as _i12;
+import 'package:playground/modules/examples/repositories/models/get_snippet_response.dart'
+    as _i6;
+import 'package:playground/modules/examples/repositories/models/save_snippet_request.dart'
+    as _i13;
+import 'package:playground/modules/examples/repositories/models/save_snippet_response.dart'
+    as _i7;
 
 // ignore_for_file: type=lint
 // ignore_for_file: avoid_redundant_argument_values
@@ -59,61 +67,81 @@ class _FakeGetExampleResponse_2 extends _i1.Fake
 
 class _FakeOutputResponse_3 extends _i1.Fake implements _i5.OutputResponse {}
 
+class _FakeGetSnippetResponse_4 extends _i1.Fake
+    implements _i6.GetSnippetResponse {}
+
+class _FakeSaveSnippetResponse_5 extends _i1.Fake
+    implements _i7.SaveSnippetResponse {}
+
 /// A class which mocks [ExampleClient].
 ///
 /// See the documentation for Mockito's code generation for more information.
-class MockExampleClient extends _i1.Mock implements _i6.ExampleClient {
+class MockExampleClient extends _i1.Mock implements _i8.ExampleClient {
   MockExampleClient() {
     _i1.throwOnMissingStub(this);
   }
 
   @override
-  _i7.Future<_i2.GetListOfExampleResponse> getListOfExamples(
-          _i8.GetListOfExamplesRequestWrapper? request) =>
+  _i9.Future<_i2.GetListOfExampleResponse> getListOfExamples(
+          _i10.GetListOfExamplesRequestWrapper? request) =>
       (super.noSuchMethod(Invocation.method(#getListOfExamples, [request]),
               returnValue: Future<_i2.GetListOfExampleResponse>.value(
                   _FakeGetListOfExampleResponse_0()))
-          as _i7.Future<_i2.GetListOfExampleResponse>);
+          as _i9.Future<_i2.GetListOfExampleResponse>);
   @override
-  _i7.Future<_i3.GetExampleCodeResponse> getExampleSource(
-          _i9.GetExampleRequestWrapper? request) =>
+  _i9.Future<_i3.GetExampleCodeResponse> getExampleSource(
+          _i11.GetExampleRequestWrapper? request) =>
       (super.noSuchMethod(Invocation.method(#getExampleSource, [request]),
               returnValue: Future<_i3.GetExampleCodeResponse>.value(
                   _FakeGetExampleCodeResponse_1()))
-          as _i7.Future<_i3.GetExampleCodeResponse>);
+          as _i9.Future<_i3.GetExampleCodeResponse>);
   @override
-  _i7.Future<_i4.GetExampleResponse> getDefaultExample(
-          _i9.GetExampleRequestWrapper? request) =>
+  _i9.Future<_i4.GetExampleResponse> getDefaultExample(
+          _i11.GetExampleRequestWrapper? request) =>
       (super.noSuchMethod(Invocation.method(#getDefaultExample, [request]),
               returnValue: Future<_i4.GetExampleResponse>.value(
                   _FakeGetExampleResponse_2()))
-          as _i7.Future<_i4.GetExampleResponse>);
+          as _i9.Future<_i4.GetExampleResponse>);
   @override
-  _i7.Future<_i4.GetExampleResponse> getExample(
-          _i9.GetExampleRequestWrapper? request) =>
+  _i9.Future<_i4.GetExampleResponse> getExample(
+          _i11.GetExampleRequestWrapper? request) =>
       (super.noSuchMethod(Invocation.method(#getExample, [request]),
               returnValue: Future<_i4.GetExampleResponse>.value(
                   _FakeGetExampleResponse_2()))
-          as _i7.Future<_i4.GetExampleResponse>);
+          as _i9.Future<_i4.GetExampleResponse>);
   @override
-  _i7.Future<_i5.OutputResponse> getExampleOutput(
-          _i9.GetExampleRequestWrapper? request) =>
+  _i9.Future<_i5.OutputResponse> getExampleOutput(
+          _i11.GetExampleRequestWrapper? request) =>
       (super.noSuchMethod(Invocation.method(#getExampleOutput, [request]),
               returnValue:
                   Future<_i5.OutputResponse>.value(_FakeOutputResponse_3()))
-          as _i7.Future<_i5.OutputResponse>);
+          as _i9.Future<_i5.OutputResponse>);
   @override
-  _i7.Future<_i5.OutputResponse> getExampleLogs(
-          _i9.GetExampleRequestWrapper? request) =>
+  _i9.Future<_i5.OutputResponse> getExampleLogs(
+          _i11.GetExampleRequestWrapper? request) =>
       (super.noSuchMethod(Invocation.method(#getExampleLogs, [request]),
               returnValue:
                   Future<_i5.OutputResponse>.value(_FakeOutputResponse_3()))
-          as _i7.Future<_i5.OutputResponse>);
+          as _i9.Future<_i5.OutputResponse>);
   @override
-  _i7.Future<_i5.OutputResponse> getExampleGraph(
-          _i9.GetExampleRequestWrapper? request) =>
+  _i9.Future<_i5.OutputResponse> getExampleGraph(
+          _i11.GetExampleRequestWrapper? request) =>
       (super.noSuchMethod(Invocation.method(#getExampleGraph, [request]),
               returnValue:
                   Future<_i5.OutputResponse>.value(_FakeOutputResponse_3()))
-          as _i7.Future<_i5.OutputResponse>);
+          as _i9.Future<_i5.OutputResponse>);
+  @override
+  _i9.Future<_i6.GetSnippetResponse> getSnippet(
+          _i12.GetSnippetRequestWrapper? request) =>
+      (super.noSuchMethod(Invocation.method(#getSnippet, [request]),
+              returnValue: Future<_i6.GetSnippetResponse>.value(
+                  _FakeGetSnippetResponse_4()))
+          as _i9.Future<_i6.GetSnippetResponse>);
+  @override
+  _i9.Future<_i7.SaveSnippetResponse> saveSnippet(
+          _i13.SaveSnippetRequestWrapper? request) =>
+      (super.noSuchMethod(Invocation.method(#saveSnippet, [request]),
+              returnValue: Future<_i7.SaveSnippetResponse>.value(
+                  _FakeSaveSnippetResponse_5()))
+          as _i9.Future<_i7.SaveSnippetResponse>);
 }
diff --git a/playground/frontend/test/pages/playground/states/example_selector_state_test.dart b/playground/frontend/test/pages/playground/states/example_selector_state_test.dart
index 0198397a795..99d847f38e0 100644
--- a/playground/frontend/test/pages/playground/states/example_selector_state_test.dart
+++ b/playground/frontend/test/pages/playground/states/example_selector_state_test.dart
@@ -19,27 +19,29 @@
 import 'package:flutter_test/flutter_test.dart';
 import 'package:mockito/annotations.dart';
 import 'package:playground/modules/examples/models/example_model.dart';
-import 'package:playground/modules/examples/repositories/example_client/example_client.dart';
-import 'package:playground/modules/examples/repositories/example_repository.dart';
+import 'package:playground/pages/playground/states/example_loaders/examples_loader.dart';
 import 'package:playground/pages/playground/states/example_selector_state.dart';
 import 'package:playground/pages/playground/states/examples_state.dart';
 import 'package:playground/pages/playground/states/playground_state.dart';
 
 import 'example_selector_state_test.mocks.dart';
 import 'mocks/categories_mock.dart';
+import 'mocks/example_repository_mock.dart';
 
-@GenerateMocks([ExampleClient])
+@GenerateMocks([ExamplesLoader])
 void main() {
   late PlaygroundState playgroundState;
   late ExampleState exampleState;
   late ExampleSelectorState state;
-  late ExampleClient client;
+  final mockExampleRepository = getMockExampleRepository();
 
   setUp(() {
-    client = MockExampleClient();
-    playgroundState = PlaygroundState();
-    exampleState = ExampleState(ExampleRepository(client));
-    state = ExampleSelectorState(exampleState, playgroundState, []);
+    exampleState = ExampleState(mockExampleRepository);
+    playgroundState = PlaygroundState(
+      examplesLoader: MockExamplesLoader(),
+      exampleState: exampleState,
+    );
+    state = ExampleSelectorState(playgroundState, []);
   });
 
   test(
@@ -105,7 +107,6 @@ void main() {
       'but should NOT:'
       '- affect Example state categories', () {
     final state = ExampleSelectorState(
-      exampleState,
       playgroundState,
       categoriesMock,
     );
@@ -125,7 +126,6 @@ void main() {
       '- be sensitive for register,'
       '- affect Example state categories', () {
     final state = ExampleSelectorState(
-      exampleState,
       playgroundState,
       categoriesMock,
     );
diff --git a/playground/frontend/test/pages/playground/states/example_selector_state_test.mocks.dart b/playground/frontend/test/pages/playground/states/example_selector_state_test.mocks.dart
index a9009516d30..12d12962c5a 100644
--- a/playground/frontend/test/pages/playground/states/example_selector_state_test.mocks.dart
+++ b/playground/frontend/test/pages/playground/states/example_selector_state_test.mocks.dart
@@ -19,23 +19,15 @@
 // in playground/test/pages/playground/states/example_selector_state_test.dart.
 // Do not manually edit this file.
 
-import 'dart:async' as _i7;
+import 'dart:async' as _i4;
 
 import 'package:mockito/mockito.dart' as _i1;
-import 'package:playground/modules/editor/repository/code_repository/code_client/output_response.dart'
+import 'package:playground/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart'
     as _i5;
-import 'package:playground/modules/examples/repositories/example_client/example_client.dart'
-    as _i6;
-import 'package:playground/modules/examples/repositories/models/get_example_code_response.dart'
-    as _i3;
-import 'package:playground/modules/examples/repositories/models/get_example_request.dart'
-    as _i9;
-import 'package:playground/modules/examples/repositories/models/get_example_response.dart'
-    as _i4;
-import 'package:playground/modules/examples/repositories/models/get_list_of_examples_request.dart'
-    as _i8;
-import 'package:playground/modules/examples/repositories/models/get_list_of_examples_response.dart'
+import 'package:playground/pages/playground/states/example_loaders/examples_loader.dart'
     as _i2;
+import 'package:playground/pages/playground/states/playground_state.dart'
+    as _i3;
 
 // ignore_for_file: type=lint
 // ignore_for_file: avoid_redundant_argument_values
@@ -47,72 +39,21 @@ import 'package:playground/modules/examples/repositories/models/get_list_of_exam
 // ignore_for_file: unnecessary_parenthesis
 // ignore_for_file: camel_case_types
 
-class _FakeGetListOfExampleResponse_0 extends _i1.Fake
-    implements _i2.GetListOfExampleResponse {}
-
-class _FakeGetExampleCodeResponse_1 extends _i1.Fake
-    implements _i3.GetExampleCodeResponse {}
-
-class _FakeGetExampleResponse_2 extends _i1.Fake
-    implements _i4.GetExampleResponse {}
-
-class _FakeOutputResponse_3 extends _i1.Fake implements _i5.OutputResponse {}
-
-/// A class which mocks [ExampleClient].
+/// A class which mocks [ExamplesLoader].
 ///
 /// See the documentation for Mockito's code generation for more information.
-class MockExampleClient extends _i1.Mock implements _i6.ExampleClient {
-  MockExampleClient() {
+class MockExamplesLoader extends _i1.Mock implements _i2.ExamplesLoader {
+  MockExamplesLoader() {
     _i1.throwOnMissingStub(this);
   }
 
   @override
-  _i7.Future<_i2.GetListOfExampleResponse> getListOfExamples(
-          _i8.GetListOfExamplesRequestWrapper? request) =>
-      (super.noSuchMethod(Invocation.method(#getListOfExamples, [request]),
-              returnValue: Future<_i2.GetListOfExampleResponse>.value(
-                  _FakeGetListOfExampleResponse_0()))
-          as _i7.Future<_i2.GetListOfExampleResponse>);
-  @override
-  _i7.Future<_i3.GetExampleCodeResponse> getExampleSource(
-          _i9.GetExampleRequestWrapper? request) =>
-      (super.noSuchMethod(Invocation.method(#getExampleSource, [request]),
-              returnValue: Future<_i3.GetExampleCodeResponse>.value(
-                  _FakeGetExampleCodeResponse_1()))
-          as _i7.Future<_i3.GetExampleCodeResponse>);
-  @override
-  _i7.Future<_i4.GetExampleResponse> getDefaultExample(
-          _i9.GetExampleRequestWrapper? request) =>
-      (super.noSuchMethod(Invocation.method(#getDefaultExample, [request]),
-              returnValue: Future<_i4.GetExampleResponse>.value(
-                  _FakeGetExampleResponse_2()))
-          as _i7.Future<_i4.GetExampleResponse>);
-  @override
-  _i7.Future<_i4.GetExampleResponse> getExample(
-          _i9.GetExampleRequestWrapper? request) =>
-      (super.noSuchMethod(Invocation.method(#getExample, [request]),
-              returnValue: Future<_i4.GetExampleResponse>.value(
-                  _FakeGetExampleResponse_2()))
-          as _i7.Future<_i4.GetExampleResponse>);
-  @override
-  _i7.Future<_i5.OutputResponse> getExampleOutput(
-          _i9.GetExampleRequestWrapper? request) =>
-      (super.noSuchMethod(Invocation.method(#getExampleOutput, [request]),
-              returnValue:
-                  Future<_i5.OutputResponse>.value(_FakeOutputResponse_3()))
-          as _i7.Future<_i5.OutputResponse>);
-  @override
-  _i7.Future<_i5.OutputResponse> getExampleLogs(
-          _i9.GetExampleRequestWrapper? request) =>
-      (super.noSuchMethod(Invocation.method(#getExampleLogs, [request]),
-              returnValue:
-                  Future<_i5.OutputResponse>.value(_FakeOutputResponse_3()))
-          as _i7.Future<_i5.OutputResponse>);
+  void setPlaygroundState(_i3.PlaygroundState? value) =>
+      super.noSuchMethod(Invocation.method(#setPlaygroundState, [value]),
+          returnValueForMissingStub: null);
   @override
-  _i7.Future<_i5.OutputResponse> getExampleGraph(
-          _i9.GetExampleRequestWrapper? request) =>
-      (super.noSuchMethod(Invocation.method(#getExampleGraph, [request]),
-              returnValue:
-                  Future<_i5.OutputResponse>.value(_FakeOutputResponse_3()))
-          as _i7.Future<_i5.OutputResponse>);
+  _i4.Future<void> load(_i5.ExamplesLoadingDescriptor? descriptor) =>
+      (super.noSuchMethod(Invocation.method(#load, [descriptor]),
+          returnValue: Future<void>.value(),
+          returnValueForMissingStub: Future<void>.value()) as _i4.Future<void>);
 }
diff --git a/playground/frontend/test/pages/playground/states/examples_state_test.dart b/playground/frontend/test/pages/playground/states/examples_state_test.dart
index 5416ff7be82..169dce298a2 100644
--- a/playground/frontend/test/pages/playground/states/examples_state_test.dart
+++ b/playground/frontend/test/pages/playground/states/examples_state_test.dart
@@ -17,15 +17,14 @@
  */
 
 import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/annotations.dart';
 import 'package:mockito/mockito.dart';
-import 'package:playground/modules/examples/repositories/example_repository.dart';
 import 'package:playground/modules/sdk/models/sdk.dart';
 import 'package:playground/pages/playground/states/examples_state.dart';
 
-import 'examples_state_test.mocks.dart';
 import 'mocks/categories_mock.dart';
 import 'mocks/example_mock.dart';
+import 'mocks/example_repository_mock.dart';
+import 'mocks/example_repository_mock.mocks.dart';
 import 'mocks/request_mock.dart';
 
 final kDefaultExamplesMapMock = {
@@ -35,13 +34,12 @@ final kDefaultExamplesMapMock = {
   SDK.scio: exampleWithAllAdditionsMock,
 };
 
-@GenerateMocks([ExampleRepository])
 void main() {
   late ExampleState state;
   late MockExampleRepository mockRepo;
 
   setUp(() {
-    mockRepo = MockExampleRepository();
+    mockRepo = getMockExampleRepository();
     state = ExampleState(mockRepo);
   });
 
@@ -148,7 +146,7 @@ void main() {
       'then loadExampleInfo should return example immediately',
       () async {
         expect(
-          await state.loadExampleInfo(exampleMock1, SDK.java),
+          await state.loadExampleInfo(exampleMock1),
           exampleMock1,
         );
       },
@@ -157,19 +155,8 @@ void main() {
     test(
       'Example state loadExampleInfo should load source, output, logs, graph for given example',
       () async {
-        // stubs
-        when(mockRepo.getExampleOutput(kRequestForExampleInfo))
-            .thenAnswer((_) async => kOutputResponseMock.output);
-        when(mockRepo.getExampleSource(kRequestForExampleInfo))
-            .thenAnswer((_) async => kOutputResponseMock.output);
-        when(mockRepo.getExampleLogs(kRequestForExampleInfo))
-            .thenAnswer((_) async => kOutputResponseMock.output);
-        when(mockRepo.getExampleGraph(kRequestForExampleInfo))
-            .thenAnswer((_) async => kOutputResponseMock.output);
-
-        // test assertion
         expect(
-          await state.loadExampleInfo(exampleWithoutSourceMock, SDK.java),
+          await state.loadExampleInfo(exampleWithoutSourceMock),
           exampleWithAllAdditionsMock,
         );
       },
@@ -190,14 +177,6 @@ void main() {
       'with all additions for every Sdk',
       () async {
         // stubs
-        when(mockRepo.getDefaultExample(kRequestDefaultExampleForJava))
-            .thenAnswer((_) async => exampleWithoutSourceMock);
-        when(mockRepo.getDefaultExample(kRequestDefaultExampleForGo))
-            .thenAnswer((_) async => exampleWithoutSourceMock);
-        when(mockRepo.getDefaultExample(kRequestDefaultExampleForPython))
-            .thenAnswer((_) async => exampleWithoutSourceMock);
-        when(mockRepo.getDefaultExample(kRequestDefaultExampleForScio))
-            .thenAnswer((_) async => exampleWithoutSourceMock);
         when(mockRepo.getExampleOutput(kRequestForExampleInfo))
             .thenAnswer((_) async => kOutputResponseMock.output);
         when(mockRepo.getExampleSource(kRequestForExampleInfo))
diff --git a/playground/frontend/test/pages/playground/states/mocks/example_mock.dart b/playground/frontend/test/pages/playground/states/mocks/example_mock.dart
index 0be09e915f0..f0833e8afb4 100644
--- a/playground/frontend/test/pages/playground/states/mocks/example_mock.dart
+++ b/playground/frontend/test/pages/playground/states/mocks/example_mock.dart
@@ -17,37 +17,51 @@
  */
 
 import 'package:playground/modules/examples/models/example_model.dart';
+import 'package:playground/modules/sdk/models/sdk.dart';
 
 final ExampleModel exampleMock1 = ExampleModel(
+  sdk: SDK.python,
   source: 'ex1',
   name: 'Example',
   type: ExampleType.example,
   description: 'description',
-  path: 'SDK/Category/Name',
+  path: 'SDK_PYTHON/Category/Name',
 );
 
 final ExampleModel exampleMock2 = ExampleModel(
+  sdk: SDK.python,
   source: 'ex2',
   name: 'Kata',
   type: ExampleType.kata,
   description: 'description',
-  path: 'SDK/Category/Name',
+  path: 'SDK_PYTHON/Category/Name',
 );
 
 final ExampleModel exampleWithoutSourceMock = ExampleModel(
+  sdk: SDK.python,
   name: 'Test example',
   type: ExampleType.example,
   description: 'description',
-  path: 'SDK/Category/Name',
+  path: 'SDK_PYTHON/Category/Name',
 );
 
 final ExampleModel exampleWithAllAdditionsMock = ExampleModel(
+  sdk: SDK.python,
   name: 'Test example',
   type: ExampleType.example,
   description: 'description',
-  path: 'SDK/Category/Name',
+  path: 'SDK_PYTHON/Category/Name',
   source: 'test outputs',
   outputs: 'test outputs',
   logs: 'test outputs',
   graph: 'test outputs',
 );
+
+final ExampleModel exampleMockGo = ExampleModel(
+  sdk: SDK.go,
+  source: 'ex1',
+  name: 'Example',
+  type: ExampleType.example,
+  description: 'description',
+  path: 'SDK_GO/Category/Name',
+);
diff --git a/playground/frontend/test/pages/playground/states/mocks/example_repository_mock.dart b/playground/frontend/test/pages/playground/states/mocks/example_repository_mock.dart
new file mode 100644
index 00000000000..e44c5f03b4e
--- /dev/null
+++ b/playground/frontend/test/pages/playground/states/mocks/example_repository_mock.dart
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+import 'package:playground/modules/examples/repositories/example_repository.dart';
+
+import 'example_repository_mock.mocks.dart';
+import 'example_mock.dart';
+import 'request_mock.dart';
+
+@GenerateMocks([ExampleRepository])
+MockExampleRepository getMockExampleRepository() {
+  final m = MockExampleRepository();
+
+  // stubs
+  when(m.getDefaultExample(kRequestDefaultExampleForJava))
+      .thenAnswer((_) async => exampleWithoutSourceMock);
+  when(m.getDefaultExample(kRequestDefaultExampleForGo))
+      .thenAnswer((_) async => exampleWithoutSourceMock);
+  when(m.getDefaultExample(kRequestDefaultExampleForPython))
+      .thenAnswer((_) async => exampleWithoutSourceMock);
+  when(m.getDefaultExample(kRequestDefaultExampleForScio))
+      .thenAnswer((_) async => exampleWithoutSourceMock);
+
+  when(m.getExampleOutput(kRequestForExampleInfo))
+      .thenAnswer((_) async => kOutputResponseMock.output);
+  when(m.getExampleSource(kRequestForExampleInfo))
+      .thenAnswer((_) async => kOutputResponseMock.output);
+  when(m.getExampleLogs(kRequestForExampleInfo))
+      .thenAnswer((_) async => kOutputResponseMock.output);
+  when(m.getExampleGraph(kRequestForExampleInfo))
+      .thenAnswer((_) async => kOutputResponseMock.output);
+
+  return m;
+}
diff --git a/playground/frontend/test/pages/playground/states/examples_state_test.mocks.dart b/playground/frontend/test/pages/playground/states/mocks/example_repository_mock.mocks.dart
similarity index 55%
rename from playground/frontend/test/pages/playground/states/examples_state_test.mocks.dart
rename to playground/frontend/test/pages/playground/states/mocks/example_repository_mock.mocks.dart
index 832064e2527..8215315d7fc 100644
--- a/playground/frontend/test/pages/playground/states/examples_state_test.mocks.dart
+++ b/playground/frontend/test/pages/playground/states/mocks/example_repository_mock.mocks.dart
@@ -16,22 +16,28 @@
  * limitations under the License.
  */
 
-// Mocks generated by Mockito 5.0.16 from annotations
-// in playground/test/pages/playground/states/examples_state_test.dart.
+// Mocks generated by Mockito 5.2.0 from annotations
+// in playground/test/pages/playground/states/mocks/example_repository_mock.dart.
 // Do not manually edit this file.
 
-import 'dart:async' as _i4;
+import 'dart:async' as _i5;
 
 import 'package:mockito/mockito.dart' as _i1;
-import 'package:playground/modules/examples/models/category_model.dart' as _i6;
+import 'package:playground/modules/examples/models/category_model.dart' as _i7;
 import 'package:playground/modules/examples/models/example_model.dart' as _i2;
 import 'package:playground/modules/examples/repositories/example_repository.dart'
-    as _i3;
+    as _i4;
 import 'package:playground/modules/examples/repositories/models/get_example_request.dart'
-    as _i8;
+    as _i9;
 import 'package:playground/modules/examples/repositories/models/get_list_of_examples_request.dart'
-    as _i7;
-import 'package:playground/modules/sdk/models/sdk.dart' as _i5;
+    as _i8;
+import 'package:playground/modules/examples/repositories/models/get_snippet_request.dart'
+    as _i10;
+import 'package:playground/modules/examples/repositories/models/get_snippet_response.dart'
+    as _i3;
+import 'package:playground/modules/examples/repositories/models/save_snippet_request.dart'
+    as _i11;
+import 'package:playground/modules/sdk/models/sdk.dart' as _i6;
 
 // ignore_for_file: type=lint
 // ignore_for_file: avoid_redundant_argument_values
@@ -45,49 +51,63 @@ import 'package:playground/modules/sdk/models/sdk.dart' as _i5;
 
 class _FakeExampleModel_0 extends _i1.Fake implements _i2.ExampleModel {}
 
+class _FakeGetSnippetResponse_1 extends _i1.Fake
+    implements _i3.GetSnippetResponse {}
+
 /// A class which mocks [ExampleRepository].
 ///
 /// See the documentation for Mockito's code generation for more information.
-class MockExampleRepository extends _i1.Mock implements _i3.ExampleRepository {
+class MockExampleRepository extends _i1.Mock implements _i4.ExampleRepository {
   MockExampleRepository() {
     _i1.throwOnMissingStub(this);
   }
 
   @override
-  _i4.Future<Map<_i5.SDK, List<_i6.CategoryModel>>> getListOfExamples(
-          _i7.GetListOfExamplesRequestWrapper? request) =>
+  _i5.Future<Map<_i6.SDK, List<_i7.CategoryModel>>> getListOfExamples(
+          _i8.GetListOfExamplesRequestWrapper? request) =>
       (super.noSuchMethod(Invocation.method(#getListOfExamples, [request]),
-              returnValue: Future<Map<_i5.SDK, List<_i6.CategoryModel>>>.value(
-                  <_i5.SDK, List<_i6.CategoryModel>>{}))
-          as _i4.Future<Map<_i5.SDK, List<_i6.CategoryModel>>>);
+              returnValue: Future<Map<_i6.SDK, List<_i7.CategoryModel>>>.value(
+                  <_i6.SDK, List<_i7.CategoryModel>>{}))
+          as _i5.Future<Map<_i6.SDK, List<_i7.CategoryModel>>>);
   @override
-  _i4.Future<_i2.ExampleModel> getDefaultExample(
-          _i8.GetExampleRequestWrapper? request) =>
+  _i5.Future<_i2.ExampleModel> getDefaultExample(
+          _i9.GetExampleRequestWrapper? request) =>
       (super.noSuchMethod(Invocation.method(#getDefaultExample, [request]),
               returnValue:
                   Future<_i2.ExampleModel>.value(_FakeExampleModel_0()))
-          as _i4.Future<_i2.ExampleModel>);
+          as _i5.Future<_i2.ExampleModel>);
   @override
-  _i4.Future<String> getExampleSource(_i8.GetExampleRequestWrapper? request) =>
+  _i5.Future<String> getExampleSource(_i9.GetExampleRequestWrapper? request) =>
       (super.noSuchMethod(Invocation.method(#getExampleSource, [request]),
-          returnValue: Future<String>.value('')) as _i4.Future<String>);
+          returnValue: Future<String>.value('')) as _i5.Future<String>);
   @override
-  _i4.Future<String> getExampleOutput(_i8.GetExampleRequestWrapper? request) =>
+  _i5.Future<String> getExampleOutput(_i9.GetExampleRequestWrapper? request) =>
       (super.noSuchMethod(Invocation.method(#getExampleOutput, [request]),
-          returnValue: Future<String>.value('')) as _i4.Future<String>);
+          returnValue: Future<String>.value('')) as _i5.Future<String>);
   @override
-  _i4.Future<String> getExampleLogs(_i8.GetExampleRequestWrapper? request) =>
+  _i5.Future<String> getExampleLogs(_i9.GetExampleRequestWrapper? request) =>
       (super.noSuchMethod(Invocation.method(#getExampleLogs, [request]),
-          returnValue: Future<String>.value('')) as _i4.Future<String>);
+          returnValue: Future<String>.value('')) as _i5.Future<String>);
   @override
-  _i4.Future<String> getExampleGraph(_i8.GetExampleRequestWrapper? request) =>
+  _i5.Future<String> getExampleGraph(_i9.GetExampleRequestWrapper? request) =>
       (super.noSuchMethod(Invocation.method(#getExampleGraph, [request]),
-          returnValue: Future<String>.value('')) as _i4.Future<String>);
+          returnValue: Future<String>.value('')) as _i5.Future<String>);
   @override
-  _i4.Future<_i2.ExampleModel> getExample(
-          _i8.GetExampleRequestWrapper? request) =>
+  _i5.Future<_i2.ExampleModel> getExample(
+          _i9.GetExampleRequestWrapper? request) =>
       (super.noSuchMethod(Invocation.method(#getExample, [request]),
               returnValue:
                   Future<_i2.ExampleModel>.value(_FakeExampleModel_0()))
-          as _i4.Future<_i2.ExampleModel>);
+          as _i5.Future<_i2.ExampleModel>);
+  @override
+  _i5.Future<_i3.GetSnippetResponse> getSnippet(
+          _i10.GetSnippetRequestWrapper? request) =>
+      (super.noSuchMethod(Invocation.method(#getSnippet, [request]),
+              returnValue: Future<_i3.GetSnippetResponse>.value(
+                  _FakeGetSnippetResponse_1()))
+          as _i5.Future<_i3.GetSnippetResponse>);
+  @override
+  _i5.Future<String> saveSnippet(_i11.SaveSnippetRequestWrapper? request) =>
+      (super.noSuchMethod(Invocation.method(#saveSnippet, [request]),
+          returnValue: Future<String>.value('')) as _i5.Future<String>);
 }
diff --git a/playground/frontend/test/pages/playground/states/mocks/request_mock.dart b/playground/frontend/test/pages/playground/states/mocks/request_mock.dart
index e6536897712..b08a81ebd55 100644
--- a/playground/frontend/test/pages/playground/states/mocks/request_mock.dart
+++ b/playground/frontend/test/pages/playground/states/mocks/request_mock.dart
@@ -37,7 +37,7 @@ final kGetExampleCodeResponseMock = GetExampleCodeResponse('test source');
 final kOutputResponseMock = OutputResponse('test outputs');
 
 final kRequestForExampleInfo =
-    GetExampleRequestWrapper('SDK/Category/Name', SDK.java);
+    GetExampleRequestWrapper('SDK_PYTHON/Category/Name', SDK.python);
 final kRequestDefaultExampleForJava = GetExampleRequestWrapper('', SDK.java);
 final kRequestDefaultExampleForGo = GetExampleRequestWrapper('', SDK.go);
 final kRequestDefaultExampleForPython =
diff --git a/playground/frontend/test/pages/playground/states/playground_state_test.dart b/playground/frontend/test/pages/playground/states/playground_state_test.dart
index d49e69d3017..7c2f7ee5bb3 100644
--- a/playground/frontend/test/pages/playground/states/playground_state_test.dart
+++ b/playground/frontend/test/pages/playground/states/playground_state_test.dart
@@ -17,16 +17,28 @@
  */
 
 import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
 import 'package:playground/modules/sdk/models/sdk.dart';
+import 'package:playground/pages/playground/states/example_loaders/examples_loader.dart';
+import 'package:playground/pages/playground/states/examples_state.dart';
 import 'package:playground/pages/playground/states/playground_state.dart';
 
 import 'mocks/example_mock.dart';
+import 'playground_state_test.mocks.dart';
 
+@GenerateMocks([ExamplesLoader, ExampleState])
 void main() {
   late PlaygroundState state;
+  final mockExamplesLoader = MockExamplesLoader();
+
+  when(mockExamplesLoader.load(any)).thenAnswer((_) async => 1);
 
   setUp(() {
-    state = PlaygroundState();
+    state = PlaygroundState(
+      examplesLoader: MockExamplesLoader(),
+      exampleState: MockExampleState(),
+    );
   });
 
   test('Initial value of SDK field should be java', () {
@@ -80,13 +92,12 @@ void main() {
   test(
     'Playground state setExample should update source and example and notify all listeners',
     () {
-      final state = PlaygroundState(sdk: SDK.go);
       state.addListener(() {
         expect(state.sdk, SDK.go);
-        expect(state.source, exampleMock1.source);
-        expect(state.selectedExample, exampleMock1);
+        expect(state.source, exampleMockGo.source);
+        expect(state.selectedExample, exampleMockGo);
       });
-      state.setExample(exampleMock1);
+      state.setExample(exampleMockGo);
     },
   );
 
@@ -100,8 +111,8 @@ void main() {
   test(
       'Playground state reset should reset source to example notify all listeners',
       () {
-    final state = PlaygroundState(sdk: SDK.go, selectedExample: exampleMock1);
-    state.setSource('source');
+    state.setExample(exampleMock1);
+    state.source = 'source';
     state.addListener(() {
       expect(state.source, exampleMock1.source);
     });
diff --git a/playground/frontend/test/pages/playground/states/playground_state_test.mocks.dart b/playground/frontend/test/pages/playground/states/playground_state_test.mocks.dart
new file mode 100644
index 00000000000..4cd040cc8a4
--- /dev/null
+++ b/playground/frontend/test/pages/playground/states/playground_state_test.mocks.dart
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Mocks generated by Mockito 5.2.0 from annotations
+// in playground/test/pages/playground/states/playground_state_test.dart.
+// Do not manually edit this file.
+
+import 'dart:async' as _i5;
+import 'dart:ui' as _i11;
+
+import 'package:mockito/mockito.dart' as _i1;
+import 'package:playground/modules/examples/models/category_model.dart' as _i9;
+import 'package:playground/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart'
+    as _i6;
+import 'package:playground/modules/examples/models/example_model.dart' as _i2;
+import 'package:playground/modules/examples/repositories/models/shared_file_model.dart'
+    as _i10;
+import 'package:playground/modules/sdk/models/sdk.dart' as _i8;
+import 'package:playground/pages/playground/states/example_loaders/examples_loader.dart'
+    as _i3;
+import 'package:playground/pages/playground/states/examples_state.dart' as _i7;
+import 'package:playground/pages/playground/states/playground_state.dart'
+    as _i4;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+
+class _FakeExampleModel_0 extends _i1.Fake implements _i2.ExampleModel {}
+
+/// A class which mocks [ExamplesLoader].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockExamplesLoader extends _i1.Mock implements _i3.ExamplesLoader {
+  MockExamplesLoader() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  void setPlaygroundState(_i4.PlaygroundState? value) =>
+      super.noSuchMethod(Invocation.method(#setPlaygroundState, [value]),
+          returnValueForMissingStub: null);
+  @override
+  _i5.Future<void> load(_i6.ExamplesLoadingDescriptor? descriptor) =>
+      (super.noSuchMethod(Invocation.method(#load, [descriptor]),
+          returnValue: Future<void>.value(),
+          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+}
+
+/// A class which mocks [ExampleState].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockExampleState extends _i1.Mock implements _i7.ExampleState {
+  MockExampleState() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  set sdkCategories(Map<_i8.SDK, List<_i9.CategoryModel>>? _sdkCategories) =>
+      super.noSuchMethod(Invocation.setter(#sdkCategories, _sdkCategories),
+          returnValueForMissingStub: null);
+  @override
+  Map<_i8.SDK, _i2.ExampleModel> get defaultExamplesMap =>
+      (super.noSuchMethod(Invocation.getter(#defaultExamplesMap),
+              returnValue: <_i8.SDK, _i2.ExampleModel>{})
+          as Map<_i8.SDK, _i2.ExampleModel>);
+  @override
+  set defaultExamplesMap(Map<_i8.SDK, _i2.ExampleModel>? _defaultExamplesMap) =>
+      super.noSuchMethod(
+          Invocation.setter(#defaultExamplesMap, _defaultExamplesMap),
+          returnValueForMissingStub: null);
+  @override
+  set defaultExample(_i2.ExampleModel? _defaultExample) =>
+      super.noSuchMethod(Invocation.setter(#defaultExample, _defaultExample),
+          returnValueForMissingStub: null);
+  @override
+  bool get isSelectorOpened =>
+      (super.noSuchMethod(Invocation.getter(#isSelectorOpened),
+          returnValue: false) as bool);
+  @override
+  set isSelectorOpened(bool? _isSelectorOpened) => super.noSuchMethod(
+      Invocation.setter(#isSelectorOpened, _isSelectorOpened),
+      returnValueForMissingStub: null);
+  @override
+  _i5.Future<void> get allExamplesFuture =>
+      (super.noSuchMethod(Invocation.getter(#allExamplesFuture),
+          returnValue: Future<void>.value()) as _i5.Future<void>);
+  @override
+  bool get hasExampleCatalog =>
+      (super.noSuchMethod(Invocation.getter(#hasExampleCatalog),
+          returnValue: false) as bool);
+  @override
+  bool get hasListeners =>
+      (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false)
+          as bool);
+  @override
+  dynamic setSdkCategories(Map<_i8.SDK, List<_i9.CategoryModel>>? map) =>
+      super.noSuchMethod(Invocation.method(#setSdkCategories, [map]));
+  @override
+  List<_i9.CategoryModel>? getCategories(_i8.SDK? sdk) =>
+      (super.noSuchMethod(Invocation.method(#getCategories, [sdk]))
+          as List<_i9.CategoryModel>?);
+  @override
+  _i5.Future<String> getExampleOutput(String? id, _i8.SDK? sdk) =>
+      (super.noSuchMethod(Invocation.method(#getExampleOutput, [id, sdk]),
+          returnValue: Future<String>.value('')) as _i5.Future<String>);
+  @override
+  _i5.Future<String> getExampleSource(String? id, _i8.SDK? sdk) =>
+      (super.noSuchMethod(Invocation.method(#getExampleSource, [id, sdk]),
+          returnValue: Future<String>.value('')) as _i5.Future<String>);
+  @override
+  _i5.Future<_i2.ExampleModel> getExample(String? path, _i8.SDK? sdk) =>
+      (super.noSuchMethod(Invocation.method(#getExample, [path, sdk]),
+              returnValue:
+                  Future<_i2.ExampleModel>.value(_FakeExampleModel_0()))
+          as _i5.Future<_i2.ExampleModel>);
+  @override
+  _i5.Future<String> getExampleLogs(String? id, _i8.SDK? sdk) =>
+      (super.noSuchMethod(Invocation.method(#getExampleLogs, [id, sdk]),
+          returnValue: Future<String>.value('')) as _i5.Future<String>);
+  @override
+  _i5.Future<String> getExampleGraph(String? id, _i8.SDK? sdk) =>
+      (super.noSuchMethod(Invocation.method(#getExampleGraph, [id, sdk]),
+          returnValue: Future<String>.value('')) as _i5.Future<String>);
+  @override
+  _i5.Future<_i2.ExampleModel> loadSharedExample(String? id) =>
+      (super.noSuchMethod(Invocation.method(#loadSharedExample, [id]),
+              returnValue:
+                  Future<_i2.ExampleModel>.value(_FakeExampleModel_0()))
+          as _i5.Future<_i2.ExampleModel>);
+  @override
+  _i5.Future<String> getSnippetId(
+          {List<_i10.SharedFile>? files,
+          _i8.SDK? sdk,
+          String? pipelineOptions}) =>
+      (super.noSuchMethod(
+          Invocation.method(#getSnippetId, [],
+              {#files: files, #sdk: sdk, #pipelineOptions: pipelineOptions}),
+          returnValue: Future<String>.value('')) as _i5.Future<String>);
+  @override
+  _i5.Future<_i2.ExampleModel> loadExampleInfo(_i2.ExampleModel? example) =>
+      (super.noSuchMethod(Invocation.method(#loadExampleInfo, [example]),
+              returnValue:
+                  Future<_i2.ExampleModel>.value(_FakeExampleModel_0()))
+          as _i5.Future<_i2.ExampleModel>);
+  @override
+  _i5.Future<void> loadDefaultExamples() =>
+      (super.noSuchMethod(Invocation.method(#loadDefaultExamples, []),
+          returnValue: Future<void>.value(),
+          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+  @override
+  _i5.Future<void> loadDefaultExamplesIfNot() =>
+      (super.noSuchMethod(Invocation.method(#loadDefaultExamplesIfNot, []),
+          returnValue: Future<void>.value(),
+          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+  @override
+  _i5.Future<_i2.ExampleModel?> getCatalogExampleByPath(String? path) =>
+      (super.noSuchMethod(Invocation.method(#getCatalogExampleByPath, [path]),
+              returnValue: Future<_i2.ExampleModel?>.value())
+          as _i5.Future<_i2.ExampleModel?>);
+  @override
+  void addListener(_i11.VoidCallback? listener) =>
+      super.noSuchMethod(Invocation.method(#addListener, [listener]),
+          returnValueForMissingStub: null);
+  @override
+  void removeListener(_i11.VoidCallback? listener) =>
+      super.noSuchMethod(Invocation.method(#removeListener, [listener]),
+          returnValueForMissingStub: null);
+  @override
+  void dispose() => super.noSuchMethod(Invocation.method(#dispose, []),
+      returnValueForMissingStub: null);
+  @override
+  void notifyListeners() =>
+      super.noSuchMethod(Invocation.method(#notifyListeners, []),
+          returnValueForMissingStub: null);
+}