AI LINE Bot練功坊-L9 Message Type (3) Flex Message
===
## 匯入函式
```python
from linebot.v3.messaging import (
FlexMessage,
FlexBubble,
FlexImage,
FlexMessage,
FlexBox,
FlexText,
FlexIcon,
FlexButton,
FlexSeparator,
)
```
# Flex Message 彈性訊息
LINE BOT 可以使用 Flex Message ( 彈性樣板訊息 ) 發送客製化的選單訊息,Flex Message 使用網頁 CSS3 的 Flex 語法,可以設計較豐富且多樣性的按鈕版型
而且不像 Template 在電腦版就無法運作,Flex Message 支援 「電腦版」
## Flex Message 結構
Flex Message 是由三層結構組成
**Container 容器 -> Block 區塊 -> Component 組件**
內容及圖片參考 [官方網站](https://developers.line.biz/en/docs/messaging-api/flex-message-elements/)。

>Reference:https://developers.line.biz/en/docs/messaging-api/using-flex-messages/
### **Container 容器**
容器是 Flex Message 的最外層結構,有兩種類型可選
| 1.Bubble | 2.Carousel |
|:--------------------------------------------------:|:--------------------------------------------------:|
| 是包含一個訊息框的容器。 | 是包含多個 Bubble 的容器,可以橫向滑動 |
|  | 
|
### **Block 區塊**
Block區塊是組成 Bubble 的結構,分為四個部分

Header: 顯示標題的區塊
Hero: 顯示主要圖片的區塊
Body: 顯示主要內容的區塊
Footer: 顯示按鈕或補充訊息的區塊
### **Component 組件**
組件是 Flex Message 的最基本結構,類型很多如下
1. Box: 定義介面的佈局,裡面可以包含其它組件
2. Button: 按鈕

3. Image: 圖片

4. Icon: 文字前的圖標

5. Text: 文字

6. Span: 單行文字,可以指定不同的顏色、大小、粗細

7. Separator: 分隔線,可製作水平或垂直的分隔線

8. Filler: 可以在組件間插入空白區塊

>Reference:https://developers.line.biz/en/docs/messaging-api/flex-message-elements/
### **Component 屬性**
**Bubble**
* direction 內容排列方向
* ltr 從左至右
* rtl 從右至左
* size 調整 bubble 大小
* giga
* mega
* kilo
* hecto
* deca
* micro
* nano
* Action Type
* postback
* uri
* message
* datetimepicker
---
**Header、Hero、Body、Footer**
* backgroundColor 背景顏色
* seperator 分隔線
* True
* False
* seperatorColor 分隔線顏色
---
**Box**
* layout
* vertical 垂直排列
box, button, image, text, separator, and filler
* horizontal 水平排列
box, button, image, text, separator, and filler
* baseline 水平排列
icon, text, and filler
---
#### baseline 與 horizontal 不同之處
baseline 中的元件按同一基線垂直對齊,這意味著所有子元件都使用相同的基線,無論字型大小。 圖示元件的基線是圖示影象的底部。

baseline 中的元件不能套用 *gravity* 和 *offsetBottom* 屬性
* position
* relative 相對配置
可以設定父層元素為基準元素作絕對位移。
* absolute 絕對配置
以「自己原本顯示的位置為基準位置」來指定上下左右的偏移,且區塊原本的空間仍會保留不會消失。
* flex 如果元件的位置屬性設定為相對(relative),則元件的寬度和高度由元件的 flex 屬性決定。
#### horizontal box 中的寬度分配
假設一個 horizontal box 有兩個元件,一個將 flex 屬性設定為 2,另一個設定為 3。 然後將可用寬度(horizontal box 的寬度)以二比三的比例劃分,並分配給每個元件。

---
#### Vertical box 中的高度分配
假設一個 horizontal box 有兩個 vertical boxes,第一個 box 佔用五行的文字,第二個 box 根據其 flex 屬性的值,以 2 : 3 的比例分配給兩個文字元件。

* spacing 元件之間的間距
本示例 Flex Message 有一個 horizontal box,其中三個 vertical boxes 作為子元件,間隔均為(md)。

* margin 元件之間的邊距 (邊距屬性優先於間距屬性)
本示例 Flex Message 有一個 horizontal box,其中三個 vertical boxes 作為子元件,間隔均為(md),第三個 vertical box 邊距屬性為 xxl。

* width
* height
* maxWidth
* maxHeight
* backgroundColor
* borderWidth
* borderColor
* cornerRadius
* justifyContent 在父階層中設置幫助安排內容
* flex-start
這將從起點開始對所有項目進行排列。如果是水平,則將基於 bubble 給出的方向: ltr (從左至右)或 rtl (從右至左);反之若是垂直,則所有項目將從上至下進行排列。
* center
將所有項目排列在中心點。
* flex-end
這將從末端開始對所有項目進行排列。如果是水平,則將基於 bubble 給出的方向: ltr 從左至右)或 rtl (從右至左);反之若是垂直,則所有項目將從下至上進行排列。
* space-between
第一個與最後一個項目貼齊左右側的佈局,其餘項目平均分配到顯示畫面的中心。
* space-around
將每個項目之間的距離均勻地分配。
* space-evenly
平均劃分每個項目與訊息佈局的距離來進行排列。
#### **layout: vertical**

---
#### **layout: horizontal & direction: ltr**

* alignItems 指定父階層中的項目更換排序方向
* flex-start
如果父階層水平排列,則所有項目將自上而下;如果父母垂直排列,則所有項目將從一開始(取決於 Bubble 的方向)。
* center
將所有項目排列在水平和垂直方向的中心點。
* flex-end
如果將父階層水平排列,則所有項目將從下至上;並且如果將父接成垂直排列,則所有項目將從末端排列(取決於 Bubble 的方向)。
#### **layout: vertical**

---
#### **layout: horizontal & direction: ltr**

* Offset 偏移量
* offsetTop
將元件從元件原始位置的頂部邊緣向下移動。
* offsetBottom
將元件從元件原始位置的底部邊緣向上移動。
* offsetStart
將元件從文字開始的地方移開。 如果 bubble 的方向是 LTR,則向右移動。 如果是 RTL,則向左移動。
* offsetEnd
遠離文字結束的地方。 如果 bubble 的方向是 LTR,則向左移動。 如果是 RTL,則向右移動。

#### Offset when position is relative

| Property | Value |
| -------- | ------ |
| position | relative|
| offsetTop| 10px |
| offStart | 40px |
---
#### Offset when position is absolute

| Property | Value |
| --------- | -------- |
| position | absolute |
| offsetTop | 10px |
| offBottom | 20px |
| offStart | 40px |
| offEnd | 80px |
* Padding 元件內所有內容與元件自身的邊界
* paddingAll
* paddingTop
* paddingBottom
* paddingStart
* paddingEnd

| Property | Value |
| --------- | -------- |
| paddingTop | 20px |
| paddingAll | 80px |
| paddingStart| 40px |
* Background Type
* linearGradient 線性漸變背景
* angle
* startColor
* endColor
* centerColor
* centerPosition
```
{
"type": "bubble",
"body": {
"type": "box",
"layout": "vertical",
"contents": [],
"background": {
"type": "linearGradient",
"angle": "0deg",
"startColor": "#ff0000",
"endColor": "#0000ff"
},
"height": "200px"
}
}
```
#### Linear gradient at 45 degrees (Bottom-left to top-right)

#### Linear gradient at 90 degrees (Left to right)

#### Linear gradient at 180 degrees (Top to bottom)

* Action Type
---
**Button**
* flex
* position
* margin
* height
* style
* link
* primary
* secondary
* color
* gravity
#### 使用 gravity 屬性,垂直對齊 text, image, 或 button

* scaling
如果縮放屬性設定為 true,您可以根據 LINE 應用程式的字型大小設定自動縮放字型大小和圖示大小。
* adjustMode
如果您為按鈕和文字元件的 adjustMode 屬性 shrink-to-fit,文字的字型大小將自動縮小以適應。

* Offset
* Action
---
**Image**
* flex
* position
* url
* margin
* align
#### 使用 align 屬性,水平對齊 text, 或 image

* gravity
* size
* aspectRatio 圖片長寬比
* aspectMode
* cover
* fit
* backgroundColor
* animated
* True
* False
* Offset
* Action
---
**Icon**
* position
* url
* margin
* size
* scaling
* aspectRatio
* Offset
---
**Text**
* text
* flex
* margin
* size
* lineSpacing 行距
* color
* weight
* regular
* bold
* style
* normal
* italic
* decoration
* none
* underline 底線
* line-through 刪除線
* position
* align
* gravity
* wrap 過長的文字自動換至下一行
* True
* False
* scaling
* maxLines
* adjustMode
* Offset
* Action Type
---
**Span**
* text
* size
* color
* weight
* style
* decoration
---
**Separator**
* margin
* color
---
**Filter**
* flex
>Reference:https://developers.line.biz/en/docs/messaging-api/flex-message-layout/
## Flex Message 設計(flex-simulator)
官方有推出輔助工具 [Flex-Simulator](https://developers.line.biz/flex-simulator/) ,可以直接在網頁上製作 Flex Message
需登入LINE Developers 帳戶

設計工具主要分成三個區塊:
* 左邊的區塊:預覽畫面 ( 傳送到 LINE 的訊息長相 )
* 中間的區塊:Flex Message 的樹狀清單結構
* 右邊的區塊:點擊中間區塊清單裡的元件,出現對應可以修改的屬性或參數

LINE官方也有提供的一些免費模板,可以根據需求改寫裡面的參數,設計成理想中的訊息
### 製作 Flex 的步驟:
你可以點右上角的「Showcase」從官方提供的模板修改寫裡面的參數,改成想要得樣式,再匯出
如果想要從零開始做的話:
1. 點擊網頁右上角的「NEW」,選擇Container建立一個包含訊息框的 Bubble or Carousel類型
2. 點選想要修改的Block 區塊
3. 點擊中間清單的元件後,就能從上方的「+」號,加入額外的元件

如:設定標題文字及背景顏色、新增圖片(設定圖片連結與大小)、新增文字(設定字體大小、粗細和對齊方式)、新增平行線(新增分隔線,並設定邊距)、新增按鈕(設定連結及按鈕樣式、添加連結到網站、或撥打電話
### 測試 Flex Message
點擊右上方的 Send 按鈕後,會出現 Register destination 的按鈕,繼續點擊後會出現 QRCode,掃描 QRCode 加入好友就能測試


### 用Python 傳 Flex Message
首先先將官方模擬器的資料複製,點選右上角的複製JSON內容,編寫完基本指令後在「contents」之處將複製的JSON內容貼上
## Flex Message 範例

首先,程式碼建立一個 URL,這個 URL 是用來指向一個圖片資源的
FlexBubble()是一個物件,其屬性包括 direction(排列方向)、hero(主要圖像)、body(內容區域)、footer(底部區域)
* contents 是用來容納各種 Flex 元件的容器
* hero:主圖像是一張圖片,其中包含 URL、尺寸、比例、顯示模式等屬性
* body:內容區域包含了標題、評分、詳細資訊等內容,使用了不同的 Flex 元件來組合顯示
* footer:底部區域包含了一個連結到電話的按鈕和一個連結到網站的按鈕,使用了 FlexButton 元件
最後,使用 line_bot_api.reply_message 函式回覆訊息,設定了回覆的內容為一個 FlexMessage,其中的內容就是前面建立的 FlexBubble 物件
**Python程式碼**
```python
url = request.url_root + '/static/Logo.jpg'
url = url.replace("http", "https")
app.logger.info("url=" + url)
bubble = FlexBubble(
direction='ltr',
hero=FlexImage(
url=url,
size='full',
aspect_ratio='20:13',
aspect_mode='cover',
action=URIAction(uri='https://www.facebook.com/NTUEBIGDATAEDU', label='label')
),
body=FlexBox(
layout='vertical',
contents=[
# title
FlexText(text='教育大數據', weight='bold', size='xl'),
# review
FlexBox(
layout='baseline',
margin='md',
contents=[
FlexIcon(size='sm', url="https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png"),
FlexIcon(size='sm', url="https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png"),
FlexIcon(size='sm', url="https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png"),
FlexIcon(size='sm', url="https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png"),
FlexIcon(size='sm', url="https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png"),
FlexText(text='5.0', size='sm', color='#999999', margin='md', flex=0)
]
),
# info
FlexBox(
layout='vertical',
margin='lg',
spacing='sm',
contents=[
FlexBox(
layout='baseline',
spacing='sm',
contents=[
FlexText(
text='Place',
color='#aaaaaa',
size='sm',
flex=1
),
FlexText(
text='Da\'an District, Taipei ',
wrap=True,
color='#666666',
size='sm',
flex=5
)
],
),
FlexBox(
layout='baseline',
spacing='sm',
contents=[
FlexText(
text='Time',
color='#aaaaaa',
size='sm',
flex=1
),
FlexText(
text="10:00 - 23:00",
wrap=True,
color='#666666',
size='sm',
flex=5,
),
],
),
],
)
],
),
footer=FlexBox(
layout='vertical',
spacing='sm',
contents=[
# callAction
FlexButton(
style='link',
height='sm',
action=URIAction(label='CALL', uri='tel:000000'),
),
# separator
FlexSeparator(),
# websiteAction
FlexButton(
style='link',
height='sm',
action=URIAction(label='WEBSITE', uri='https://www.facebook.com/NTUEBIGDATAEDU')
)
]
),
)
line_bot_api.reply_message(
ReplyMessageRequest(
reply_token=event.reply_token,
messages=[FlexMessage(alt_text="hello", contents=bubble)]
)
)
```
>Reference:https://developers.line.biz/en/reference/messaging-api/#flex-message
Youtube 課程影片
---
<iframe width="560" height="315" src="https://www.youtube.com/embed/8p-uWpy-Uh4?si=AmQ7pvG7yvvs7dTq" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
{%hackmd @ntuebigdata/about %}
## 相關教材連結
<div style="display: flex; justify-content:space-between;">
<div>
<a class="btn btn-warning" href="https://hackmd.io/@ntuebigdata/message-type-2-template-message" style="color:white;width:300px;text-overflow:ellipsis;overflow:hidden">◀◀◀ L08 Message Type(2) Template Message ◀◀◀</a>
</div>
<div>
<a class="btn btn-info" href="https://hackmd.io/@ntuebigdata/message-type-4-imagemap-message" style="color:white;width:300px;text-overflow:ellipsis;overflow:hidden">▶▶▶ L10 Message Type(4) Imagemap Message ▶▶▶</a>
</div>
</div>