Skip to content

Feat/link preview package#790

Merged
demchenkoalex merged 23 commits intoflyerhq:mainfrom
nicolasbraun:feat/link_preview_package
Jul 12, 2025
Merged

Feat/link preview package#790
demchenkoalex merged 23 commits intoflyerhq:mainfrom
nicolasbraun:feat/link_preview_package

Conversation

@nicolasbraun
Copy link
Copy Markdown
Contributor

As discused in the former link_preview repo flyerhq/flutter_link_previewer#63 this PR aims at

  • moving the preview as a package in this monorepo
  • removing dependency to flutter_chat_types
  • adapting the style to fit what messaging apps usually do

Sidenote

@demchenkoalex
I had to rewrite the SimpleTextMessageand FlyerTextMessage to account for both the timeAndStatus and the previex position.

Also i noticed in your Stack you had an hidden timeAndStatus

Opacity(opacity: 0, child: timeAndStatus),

I suspect it was to make the bubble big enough for the time but this has no effect if not put inside a column (it stacks on the textContent).
I think it actually worked because of this line

padding: EdgeInsets.only(bottom: textStyle?.lineHeight ?? 0),

I used the same trick

            if (timeAndStatusPosition == TimeAndStatusPosition.end)
              SizedBox(height: textStyle?.lineHeight ?? 0),

but if you prefer we can add a hidden timeAndStatus so we are sure it's the proper size

Screens

Simulator Screenshot - iPhone 16 Plus - 2025-05-23 at 19 23 21
Simulator Screenshot - iPhone 16 Plus - 2025-05-23 at 19 23 18
Simulator Screenshot - iPhone 16 Plus - 2025-05-23 at 19 23 14

@nicolasbraun
Copy link
Copy Markdown
Contributor Author

PS : maybe we can could expose the message backGroundColor and/or the isSentbyMe in the builder to adapt colors to the chatMessage.

I did not want to make this package use Provider and recalculate colors based on ChatTheme to make it standalone but we could

@demchenkoalex
Copy link
Copy Markdown
Member

Also i noticed in your Stack you had an hidden timeAndStatus

This was done to allow the time and status indicators to stay aligned at the end, without forcing the message bubble to grow to full width. I literally tried everything, and the best suggestion I found was to use IntrinsicSize- which we can’t use due to performance focus.

Eventually, I came across this small article that saved me - it explains the issue really well:
https://shahadat-shaki.medium.com/align-a-widget-left-right-of-a-column-with-dynamic-width-b0b7cede8708

The same problem actually happens with link previews too. If you open the app on macOS, for example, the link previews are huge 😅 That’s why I added constraints - people will likely want to set them if they’re targeting desktop (or even web, if they serve previews from their own backend).

Unless… maybe we introduce a maxWidth parameter directly in the link preview widget? That could make it easier to manage in one place (via the builder).

PS : maybe we can could expose the message backgroundColor and/or the isSentbyMe in the builder to adapt colors to the chatMessage.

Yes, I was thinking about exposing isSentByMe for all messages, since it’s a common requirement for customization. I’ll look into it this weekend.

As for background color - I’m not so sure. I’d prefer not to expose UI-specific details in the builders. isSentByMe should be enough (yes, I know it would be easier to apply opacity to a known color, but in examples we can just use the message color from the theme, and ideally people will replace it with their own if they customize bubble colors when they notice preview looks ugly - will also add documentation article ofc).

@nicolasbraun
Copy link
Copy Markdown
Contributor Author

This was done to allow the time and status indicator

Ok it was to constrain the width when I though it was for the height
In that case I'm not realy sure why he uses this Opacity trick for width and just a bottom Padding to ensure height is also ok. I'll update my code using a column to ensure both width and height are ok and we will see how it works. Maybe it breaks alignments somehow (I have no clue how chat align in right to left languages 🙃 )

I think I actually have same problem for reactions. If message is short with many reactions. Will have to test.

LinkPreview

  • will add maxWidth, maxHeight , maxTitleLines, maxDescriptionLines. should be enough for anyone to customize in the builder based on the context (plateform, MediaQuery.sizeOf or whatever)

For the color I dont have any better idea. You are correct someone overriding chatBubble will likely have his own helper methods to calculate both color (since ChatTheme uses "regular" props (surface, container..). and not custom ones (what would make the intent clearer but required to set up more props).

Maybe we can add getters on ChatTheme to retrieve sentMessageBackgroundColor, receivedMessageBackGroundColor etc ..

From my perspective If I set up a custom ChatTheme and use flyerchat's widgets I'd expect everything to work out of the box (I I do not override colors in the Message's builders) but there is no perfect solution

  1. The LinkPreview must read ChatTheme, making it less standalone but if there is no ChatTheme in the context i'll still work so I think that's ok.
  2. We could have 2 widget LinkPreview and FlyerChatLinkPreview which is basically a wrapper around it listening to the theme
  3. We keep it like this enhancing chatTheme to make it easier to access specific colors (up to you to change the color props names, I'll start with getters for now)

@nicolasbraun
Copy link
Copy Markdown
Contributor Author

MacOS with width constrain of MediaQuery 10%

Capture d’écran 2025-05-24 à 09 11 35

@nicolasbraun nicolasbraun force-pushed the feat/link_preview_package branch from ce0f8e7 to c4f9239 Compare June 6, 2025 17:05
@nicolasbraun
Copy link
Copy Markdown
Contributor Author

Fixes + rebase

@nicolasbraun nicolasbraun force-pushed the feat/link_preview_package branch from 52e98ca to afe64c8 Compare June 9, 2025 07:39
@nicolasbraun
Copy link
Copy Markdown
Contributor Author

About 0aba7f1

  • We were parsing and crashing before checking the content was UTF-8
  • Also weirdly https://google.com returns UTF-8 in meta, but charset=ISO-8859-1 is headers, i'm using header as the source of thruth.

See headers and then last line here

nicolasbraun@MacBook-Air-de-Nicolas-2:~ » curl -i https://google.com
HTTP/2 301
location: https://www.google.com/
content-type: text/html; charset=UTF-8
content-security-policy-report-only: object-src 'none';base-uri 'self';script-src 'nonce-SGJRSDysH3gdfixMBrd-Ig' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp
date: Tue, 10 Jun 2025 07:19:10 GMT
expires: Thu, 10 Jul 2025 07:19:10 GMT
cache-control: public, max-age=2592000
server: gws
content-length: 220
x-xss-protection: 0
x-frame-options: SAMEORIGIN
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000

HTTP/2 200
date: Tue, 10 Jun 2025 07:19:10 GMT
expires: -1
cache-control: private, max-age=0
content-type: text/html; charset=ISO-8859-1
content-security-policy-report-only: object-src 'none';base-uri 'self';script-src 'nonce-MBktPnfx0td5C4M7rU-5VA' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp
accept-ch: Sec-CH-Prefers-Color-Scheme
p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
server: gws
x-xss-protection: 0
x-frame-options: SAMEORIGIN
set-cookie: AEC=AVh_V2igd9mpdmDXMAba-Gmhh-0CDtnb8kauJ-lmrcDOF4phg5X3dV5hpA; expires=Sun, 07-Dec-2025 07:19:10 GMT; path=/; domain=.google.com; Secure; HttpOnly; SameSite=lax
set-cookie: __Secure-ENID=28.SE=Utw1JaM8MmMs3oDTRZfzQJXChEcnDbCpdP-J1Hzy1v2PE7PG68wiaQqUvSoSrRT_8tPdK7WHyOvcIfpRl7BbxMY9H2s9cYUeLj0M_isYAk9SRffE-oWuGao1qDOoXuFwLNHYvf8OkILvwfNKyV_RUt962f3sqf_f0wDdNLHDeDHlZNrMcRi0u7VhfAqFT1MLNLHSp_XyiJIh-N5c1qB7dn4x5LfhmxEwfQDNKd0NDxxI-4n3s1GUpdYhP31ImkiMzQ; expires=Fri, 10-Jul-2026 23:37:28 GMT; path=/; domain=.google.com; Secure; HttpOnly; SameSite=lax
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
accept-ranges: none
vary: Accept-Encoding

<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="fr"><head><meta content="text/html; charset=UTF-8". 

Regarding 33e583f

  • Also on google I encountered issues because the retrieved images has a relative path src="/images/branding/googlelogo/1x/googlelogo_white_background_color_272x92dp.pngand http.get helpers does not return the final url after redirections.

Finally and has a side note, i'm not sure if we should return the redirected (and proxied) redirectUrl has the PreviewData link or not.

@demchenkoalex demchenkoalex force-pushed the feat/link_preview_package branch from 9d47d14 to 0364318 Compare July 2, 2025 20:48
@demchenkoalex demchenkoalex force-pushed the feat/link_preview_package branch from 0364318 to f2eda49 Compare July 11, 2025 21:45
@demchenkoalex
Copy link
Copy Markdown
Member

had to re-write the widget from scratch as I didn't like that it was stretching for the whole width, so I added support of min and max width of the preview itself, using calculation of the parent text. From my testing everything is working nicely! I also changed package name to the original one to keep the same project on pub.dev

@demchenkoalex demchenkoalex merged commit 2938f64 into flyerhq:main Jul 12, 2025
1 check passed
@nicolasbraun nicolasbraun deleted the feat/link_preview_package branch July 16, 2025 07:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants