# Flutter InAppWebView 연동

Flutter InAppWebView를 사용하는 개발 환경에서 전면, 전면 비디오, 리워드 비디오, 비디오 믹스를 연동하고자 할 경우에는 아래의 과정으로 연동을 진행합니다.

## 1. 기본 준비사항

### 1.1 flutter plugin 설치

아래 연동 진행을 위해서는 애드팝콘에서 제공하는 adpopcornssp\_flutter 플러그인 설치는 필수입니다.&#x20;

아래 링크의 가이드를 보고 adpopcornssp\_flutter 를 설치하세요.

{% content-ref url="/pages/dOf1t1Si9NBLabXvTd6K" %}
[Flutter](/ssp-sdk/sdk/flutter.md)
{% endcontent-ref %}

### 1.2 flutter\_inappwebview 선언 및 연결

&#x20;아래 샘플을 참고하여, AdPopcornInAppWebViewBridgeHandler에 InAppWebViewController 를 연결합니다.

```dart
late AdPopcornInAppWebViewBridgeHandler bridgeHandler;
InAppWebViewController? webViewController;
...
  @override
  void initState() {
    super.initState();
    bridgeHandler = AdPopcornInAppWebViewBridgeHandler();
  }
....

  @override
  Widget build(BuildContext context){
  
    onWebViewCreated: (controller) {
      webViewController = controller;
      bridgeHandler.setWebViewController(controller);
      bridgeHandler.registerJavaScriptHandlers(controller);
    },
    
  }
...
```

전체 예시>&#x20;

```dart
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io' show Platform;
import 'package:flutter/services.dart';
import 'package:adpopcornssp_flutter/adpopcornssp_flutter.dart';
import 'adpopcornssp_flutter_inappwebview_handler.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'dart:convert';

void main() {
  runApp(const AdPopcornWebViewPage());
}
class AdPopcornWebViewPage extends StatefulWidget {
  const AdPopcornWebViewPage({super.key});

  @override
  State<AdPopcornWebViewPage> createState() => _AdPopcornWebViewPageState();
}

class _AdPopcornWebViewPageState extends State<AdPopcornWebViewPage> {
  InAppWebViewController? webViewController;
  late AdPopcornInAppWebViewBridgeHandler bridgeHandler;
  bool isLoading = true;
  String eventLog = '';

  @override
  void initState() {
    super.initState();
    bridgeHandler = AdPopcornInAppWebViewBridgeHandler();
  }
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
      appBar: AppBar(
        title: const Text('AdPopcorn SSP Test'),
        backgroundColor: const Color(0xFF667EEA),
        actions: [
          if (isLoading)
            const Padding(
              padding: EdgeInsets.all(16.0),
              child: SizedBox(
                width: 20,
                height: 20,
                child: CircularProgressIndicator(
                  strokeWidth: 2,
                  valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
                ),
              ),
            ),
          IconButton(
            icon: const Icon(Icons.refresh),
            onPressed: () {
              webViewController?.reload();
            },
          ),
        ],
      ),
      body: InAppWebView(
        initialData: InAppWebViewInitialData(
          data: _generateHtmlContent(),
        ),

        initialOptions: InAppWebViewGroupOptions(
          crossPlatform: InAppWebViewOptions(
            javaScriptEnabled: true,
            useShouldOverrideUrlLoading: false,
            clearCache: false,
            cacheEnabled: true,
            supportZoom: true,
            useOnLoadResource: false,
            useOnDownloadStart: false,
            mediaPlaybackRequiresUserGesture: false,
          ),
          android: AndroidInAppWebViewOptions(
            useHybridComposition: true,
            domStorageEnabled: true,
            allowContentAccess: true,
            allowFileAccess: true,
            useWideViewPort: true,
            loadWithOverviewMode: true,
            builtInZoomControls: true,
            displayZoomControls: false,
          ),
          ios: IOSInAppWebViewOptions(
            allowsInlineMediaPlayback: true,
            allowsBackForwardNavigationGestures: true,
          ),
        ),

        onWebViewCreated: (controller) {
          webViewController = controller;
          bridgeHandler.setWebViewController(controller);
          bridgeHandler.registerJavaScriptHandlers(controller);
        },

        onLoadStart: (controller, url) {
          setState(() {
            isLoading = true;
          });
        },

        onLoadStop: (controller, url) {
          setState(() {
            isLoading = false;
          });
        },

        onLoadError: (controller, url, code, message) {
          setState(() {
            isLoading = false;
          });
        },

        onLoadHttpError: (controller, url, statusCode, description) {
          setState(() {
            isLoading = false;
          });
        },

        onConsoleMessage: (controller, consoleMessage) {
          print('Console: ${consoleMessage.message}');
        },
      ),
    ));
  }
```

### 1.3 adpopcornssp\_flutter\_inappwebview\_handler.dart 추가

```dart
import 'package:flutter/services.dart';
import 'dart:convert';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:adpopcornssp_flutter/adpopcornssp_flutter.dart';

class AdPopcornInAppWebViewBridgeHandler {
  InAppWebViewController? _webViewController;

  AdPopcornInAppWebViewBridgeHandler();

  void setWebViewController(InAppWebViewController controller) {
    _webViewController = controller;
  }

  String returnMessageFormat(String eventName, Map<String, dynamic> eventData) {
    final messageMap = {
      "EventName": eventName,
      "Data": jsonEncode(eventData),
    };
    return jsonEncode(messageMap);
  }

  String publishEvent(String functionName, String data) {
    final jsonStringLiteral = jsonEncode(data);
    return """
      (function() {
        try {
          const event = new CustomEvent("$functionName", {
            detail: { data: $jsonStringLiteral }
          });
          window.dispatchEvent(event);
        } catch(e) {
          console.error('Error dispatching event:', e);
        }
      })();
    """;
  }

  void _handleNativeEvent(String data) {
    if (_webViewController != null) {
      _webViewController!.evaluateJavascript(
        source: publishEvent("NativeEvent", data),
      );
    }
  }

  void registerJavaScriptHandlers(InAppWebViewController controller) {
    _webViewController = controller;

    controller.addJavaScriptHandler(
      handlerName: 'init',
      callback: (args) {
        String appKey = args[0] as String;
        AdPopcornSSP.init(appKey);
        AdPopcornSSP.onInitializeListener = () {
          _handleNativeEvent(returnMessageFormat("AdPopcornSSPSDKDidInitialize", {}));
        };
        return null;
      },
    );

    controller.addJavaScriptHandler(
      handlerName: 'setUserId',
      callback: (args) {
        String userId = args[0] as String;
        AdPopcornSSP.setUserId(userId);
        return null;
      },
    );

    controller.addJavaScriptHandler(
      handlerName: 'loadInterstitial',
      callback: (args) {
        String appKey = args[0] as String;
        String placementId = args[1] as String;
        AdPopcornSSP.loadInterstitial(appKey, placementId);
        AdPopcornSSP.interstitialAdLoadSuccessListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          print('[AdPopcornSSP] interstitialAdLoadSuccessListener called: $placementId');
          _handleNativeEvent(returnMessageFormat("APSSPInterstitialAdLoadSuccess", dataJson));
        };
        AdPopcornSSP.interstitialAdLoadFailListener = (placementId, errorCode) {
          final dataJson = {
            "placementId": placementId,
            "errorCode": errorCode
          };
          _handleNativeEvent(returnMessageFormat("APSSPInterstitialAdLoadFail", dataJson));
        };
        print('[AdPopcornSSP] loadInterstitial called: $placementId');
        return null;
      },
    );

    controller.addJavaScriptHandler(
      handlerName: 'showInterstitial',
      callback: (args) {
        String appKey = args[0] as String;
        String placementId = args[1] as String;
        AdPopcornSSP.showInterstitial(appKey, placementId);
        AdPopcornSSP.interstitialAdShowSuccessListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPInterstitialAdShowSuccess", dataJson));
        };
        AdPopcornSSP.interstitialAdShowFailListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPInterstitialAdShowFail", dataJson));
        };
        AdPopcornSSP.interstitialAdClickedListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPInterstitialAdClicked", dataJson));
        };
        AdPopcornSSP.interstitialAdClosedListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPInterstitialAdClosed", dataJson));
        };
        return null;
      },
    );

    controller.addJavaScriptHandler(
      handlerName: 'loadInterstitialVideo',
      callback: (args) {
        String appKey = args[0] as String;
        String placementId = args[1] as String;
        AdPopcornSSP.loadInterstitialVideo(appKey, placementId);
        AdPopcornSSP.interstitialVideoAdLoadSuccessListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPInterstitialVideoAdLoadSuccess", dataJson));
        };
        AdPopcornSSP.interstitialVideoAdLoadFailListener = (placementId, errorCode) {
          final dataJson = {
            "placementId": placementId,
            "errorCode": errorCode
          };
          _handleNativeEvent(returnMessageFormat("APSSPInterstitialVideoAdLoadFail", dataJson));
        };
        return null;
      },
    );

    controller.addJavaScriptHandler(
      handlerName: 'showInterstitialVideo',
      callback: (args) {
        String appKey = args[0] as String;
        String placementId = args[1] as String;
        AdPopcornSSP.showInterstitialVideo(appKey, placementId);
        AdPopcornSSP.interstitialVideoAdShowSuccessListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPInterstitialVideoAdShowSuccess", dataJson));
        };
        AdPopcornSSP.interstitialVideoAdShowFailListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPInterstitialVideoAdShowFail", dataJson));
        };
        AdPopcornSSP.interstitialVideoAdClosedListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPInterstitialVideoAdClosed", dataJson));
        };
        return null;
      },
    );

    controller.addJavaScriptHandler(
      handlerName: 'loadRewardVideo',
      callback: (args) {
        String appKey = args[0] as String;
        String placementId = args[1] as String;
        AdPopcornSSP.loadRewardVideo(appKey, placementId);
        AdPopcornSSP.rewardVideoAdLoadSuccessListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPRewardVideoAdLoadSuccess", dataJson));
        };
        AdPopcornSSP.rewardVideoAdLoadFailListener = (placementId, errorCode) {
          final dataJson = {
            "placementId": placementId,
            "errorCode": errorCode
          };
          _handleNativeEvent(returnMessageFormat("APSSPRewardVideoAdLoadFail", dataJson));
        };
        return null;
      },
    );

    controller.addJavaScriptHandler(
      handlerName: 'showRewardVideo',
      callback: (args) {
        String appKey = args[0] as String;
        String placementId = args[1] as String;
        AdPopcornSSP.showRewardVideo(appKey, placementId);
        AdPopcornSSP.rewardVideoAdShowSuccessListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPRewardVideoAdShowSuccess", dataJson));
        };
        AdPopcornSSP.rewardVideoAdShowFailListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPRewardVideoAdShowFail", dataJson));
        };
        AdPopcornSSP.rewardVideoAdClosedListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPRewardVideoAdClosed", dataJson));
        };
        AdPopcornSSP.rewardVideoAdCompletedListener = (placementId, adNetworkNo, completed) {
          final dataJson = {
            "placementId": placementId,
	    "adNetworkNo": adNetworkNo,
  	    "completed": completed
          };
          _handleNativeEvent(returnMessageFormat("APSSPRewardVideoAdPlayCompleted", dataJson));
        };
        AdPopcornSSP.rewardPlusCompletedListener = (result, resultCode, reward) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPRewardPlusCompleted", dataJson));
        };
        return null;
      },
    );

    controller.addJavaScriptHandler(
      handlerName: 'loadVideoMix',
      callback: (args) {
        String appKey = args[0] as String;
        String placementId = args[1] as String;
        AdPopcornSSP.loadVideoMix(appKey, placementId);
        AdPopcornSSP.videoMixAdLoadSuccessListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPVideoMixAdLoadSuccess", dataJson));
        };
        AdPopcornSSP.videoMixAdLoadFailListener = (placementId, errorCode) {
          final dataJson = {
            "placementId": placementId,
            "errorCode": errorCode
          };
          _handleNativeEvent(returnMessageFormat("APSSPVideoMixAdLoadFail", dataJson));
        };
        return null;
      },
    );

    controller.addJavaScriptHandler(
      handlerName: 'showVideoMix',
      callback: (args) {
        String appKey = args[0] as String;
        String placementId = args[1] as String;
        AdPopcornSSP.showVideoMix(appKey, placementId);
        AdPopcornSSP.videoMixAdShowSuccessListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPVideoMixAdShowSuccess", dataJson));
        };
        AdPopcornSSP.videoMixAdShowFailListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPVideoMixAdShowFail", dataJson));
        };
        AdPopcornSSP.videoMixAdClosedListener = (placementId, campaignType) {
          final dataJson = {
            "placementId": placementId,
	    "campaignType": campaignType
          };
          _handleNativeEvent(returnMessageFormat("APSSPVideoMixAdClosed", dataJson));
        };
        AdPopcornSSP.videoMixAdCompletedListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPVideoMixAdPlayCompleted", dataJson));
        };
        AdPopcornSSP.videoMixAdClickedListener = (placementId) {
          final dataJson = {
            "placementId": placementId
          };
          _handleNativeEvent(returnMessageFormat("APSSPVideoMixAdClicked", dataJson));
        };
        return null;
      },
    );
  }
}

```

## 2. 웹 Javacript API 연동

위의 기본 연동이 끝난 상태에서 실제 웹 페이지에선 아래의 API를 호출하여 광고 연동 및 이벤트 연동을 진행 합니다.

### 2.1 전면 광고

#### 2.1.1 전면 광고 요청

* 전면 형태의 광고 요청이 필요할 때 사용되는 API 입니다.

```javascript
window.flutter_inappwebview.callHandler('loadInterstitial', APP_KEY, PLACEMENT_ID_INTERSTITIAL);
```

* appKey : 앱키
* placementId: 지면키

#### 2.1.2 전면 광고 노출

* 전면 광고 노출 시 사용되는 API 입니다.

```javascript
window.flutter_inappwebview.callHandler('showInterstitial', APP_KEY, PLACEMENT_ID_INTERSTITIAL);
```

* appKey : 앱키
* placementId: 지면키

#### 2.1.3 전면 광고 이벤트

* 전면 광고 관련 이벤트를 받고자 할 때 사용됩니다.

```html
<body>
  <!-- ... -->
  <script>
    window.addEventListener('NativeEvent', function(event) {
        const eventDataString = event.detail.data;
        let eventObject = null;
        
        if (typeof eventDataString === 'string') {
            try {
                eventObject = JSON.parse(eventDataString);
            } catch (e) {
                console.error('JSON parsing failed:', e);
                console.log('Failed to parse string:', eventDataString);
            }
        }
        
        if (eventObject) {
            console.log('EventName received:', eventObject.EventName);
            logEvent('Received', eventObject);
            handleAdEvent(eventObject);
        }
    });
  </script>
</body>
```

* 지원되는 EventName
  * APSSPInterstitialAdLoadSuccess : 전면 광고 로드 성공
    * placementId
  * APSSPInterstitialAdLoadFail : 전면 광고 로드 실패
    * placementId
    * errorCode
  * APSSPInterstitialAdShowSuccess : 전면 광고 노출 성공
    * placementId
  * APSSPInterstitialAdShowFail : 전면 광고 노출 실패
    * placementId
  * APSSPInterstitialAdClosed : 전면 광고 닫기
    * placementId
  * APSSPInterstitialAdClicked : 전면 광고 클릭
    * placementId

### 2.2 전면 비디오 광고

#### 2.2.1 전면 비디오광고 요청

* 전면 형태의 비디오광고 요청이 필요할 때 사용되는 API 입니다.

```javascript
window.flutter_inappwebview.callHandler('loadInterstitialVideo', APP_KEY, PLACEMENT_ID_INTERSTITIAL_VIDEO); 
```

* appKey : 앱키
* placementId: 지면키

#### 2.2.2 전면 비디오광고 노출

* 전면 비디오광고 노출 시 사용되는 API 입니다.

```javascript
window.flutter_inappwebview.callHandler('showInterstitialVideo', APP_KEY, PLACEMENT_ID_INTERSTITIAL_VIDEO);
```

* appKey : 앱키
* placementId: 지면키

#### 2.2.3 전면 비디오 광고 이벤트

* 전면 비디오광고 관련 이벤트를 받고자 할 때 사용됩니다.

```html
<body>
  <!-- ... -->
  <script>
    window.addEventListener('NativeEvent', function(event) {
        const eventDataString = event.detail.data;
        let eventObject = null;
        
        if (typeof eventDataString === 'string') {
            try {
                eventObject = JSON.parse(eventDataString);
            } catch (e) {
                console.error('JSON parsing failed:', e);
                console.log('Failed to parse string:', eventDataString);
            }
        }
        
        if (eventObject) {
            console.log('EventName received:', eventObject.EventName);
            logEvent('Received', eventObject);
            handleAdEvent(eventObject);
        }
    });
  </script>
</body>
```

* 지원되는 이벤트
  * APSSPInterstitialVideoAdLoadSuccess  : 전면 비디오 광고 로드 성공
    * placementId
  * APSSPInterstitialVideoAdLoadFail : 전면 비디오 광고 로드 실패
    * placementId
    * errorCode
  * APSSPInterstitialVideoAdShowSuccess : 전면 비디오 광고 노출 성공
    * placementId
  * APSSPInterstitialVideoAdShowFail : 전면 비디오 광고 노출 실패
    * placementId
    * errorCode
  * APSSPInterstitialVideoAdClosed : 전면 비디오 광고 닫기

    * placementId

### 2.3 리워드 비디오 광고

#### 2.3.1 리워드 비디오광고 요청

* 리워드 비디오광고 요청이 필요할 때 사용되는 API 입니다.

```javascript
window.flutter_inappwebview.callHandler('loadRewardVideo', APP_KEY, PLACEMENT_ID_REWARD_VIDEO);
```

* appKey : 앱키
* placementId: 지면키

#### 2.3.2 리워드 비디오광고 노출

* 리워드 비디오광고 노출 시 사용되는 API 입니다.

```javascript
window.flutter_inappwebview.callHandler('showRewardVideo', APP_KEY, PLACEMENT_ID_REWARD_VIDEO); 
```

* appKey : 앱키
* placementId: 지면키

#### 2.3.3 리워드비디오 광고 이벤트

* 리워드비디오광고 관련 이벤트를 받고자 할 때 사용됩니다.

```html
<body>
  <!-- ... -->
  <script>
    window.addEventListener('NativeEvent', function(event) {
        const eventDataString = event.detail.data;
        let eventObject = null;
        
        if (typeof eventDataString === 'string') {
            try {
                eventObject = JSON.parse(eventDataString);
            } catch (e) {
                console.error('JSON parsing failed:', e);
                console.log('Failed to parse string:', eventDataString);
            }
        }
        
        if (eventObject) {
            console.log('EventName received:', eventObject.EventName);
            logEvent('Received', eventObject);
            handleAdEvent(eventObject);
        }
    });
  </script>
</body>
```

* 지원되는 이벤트
  * APSSPRewardVideoAdLoadSuccess : 리워드비디오 광고 로드 성공
    * placementId
  * APSSPRewardVideoAdLoadFail : 리워드 비디오 광고 로드 실패
    * placementId
    * errorCode
  * APSSPRewardVideoAdShowSuccess : 리워드 비디오 광고 노출 성공
    * placementId
  * APSSPRewardVideoAdShowFail : 리워드 비디오 광고 노출 실패
    * placementId
    * errorCode
  * APSSPRewardVideoAdPlayCompleted : 리워드 비디오 광고 재생 완료
    * placementId
    * adNetworkNo
    * completed
  * APSSPRewardVideoAdClosed : 리워드 비디오 광고 닫기
    * placementId

### 2.4 비디오 믹스 광고

#### 2.4.1 비디오 믹스광고 요청

* 비디오 믹스 광고 요청이 필요할 때 사용되는 API 입니다.

```javascript
window.flutter_inappwebview.callHandler('loadVideoMix', APP_KEY, PLACEMENT_ID_VIDEO_MIX);
```

* appKey : 앱키
* placementId: 지면키

#### 2.4.2 비디오 믹스광고 노출

* 비디오 믹스광고 노출 시 사용되는 API 입니다.

```javascript
window.flutter_inappwebview.callHandler('showVideoMix', APP_KEY, PLACEMENT_ID_VIDEO_MIX);
```

* appKey : 앱키
* placementId: 지면키

#### 2.4.3 비디오 믹스광고 이벤트

* 비디오 믹스광고 관련 이벤트를 받고자 할 때 사용됩니다.

```html
<body>
  <!-- ... -->
  <script>
    window.addEventListener('NativeEvent', function(event) {
        const eventDataString = event.detail.data;
        let eventObject = null;
        
        if (typeof eventDataString === 'string') {
            try {
                eventObject = JSON.parse(eventDataString);
            } catch (e) {
                console.error('JSON parsing failed:', e);
                console.log('Failed to parse string:', eventDataString);
            }
        }
        
        if (eventObject) {
            console.log('EventName received:', eventObject.EventName);
            logEvent('Received', eventObject);
            handleAdEvent(eventObject);
        }
    });
  </script>
</body>
```

* 지원되는 이벤트
  * APSSPVideoMixAdLoadSuccess : 비디오 믹스 광고 로드 성공
    * placementId
  * APSSPVideoMixAdLoadFail : 비디오 믹스광고 로드 실패
    * placementId
    * errorCode
  * APSSPVideoMixAdShowSuccess : 비디오 믹스광고 노출 성공
    * placementId
  * APSSPVideoMixAdShowFail : 비디오 믹스광고 노출 실패
    * placementId
    * errorCode
  * APSSPVideoMixAdPlayCompleted  : 비디오 믹스광고 재생 완료
    * placementId
  * APSSPVideoMixAdClosed : 비디오 믹스광고 닫기
    * placementId
    * campaignType : 2(전면), 4(리워드 비디오), 6(전면 비디오)
  * APSSPVideoMixAdClicked : 비디오 믹스 광고 클릭
    * placementId


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://adpopcornssp.gitbook.io/ssp-sdk/sdk/flutter/flutter-inappwebview.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
