This is not readily available in Magento 2. Neither there are any 3rd party extensions. We had similar requirement from one of our client. Initially, we started with Magento but had to switch to other technology later on due to time constraints.
You can use below sites for reference flow available in other frameworks
https://mrbchocolates.com/collections/chocolate-bars
https://multiship.myshopify.com/collections/all
Magento's inbuilt multishipping option provides to add one address per item. It creates a new order for each shipping address. You will have to rewrite entire multishipping module. Especially to merge them into a single order is an uphill task. Besides not all payment methods support multiple addresses.
However you can follow below steps to begin with
Override entire multishipping module vendor/magento/module-multishipping/ to app/code/Vendor/Module/ and customize it as per your needs

In file vendor/magento/module-multishipping/Model/Checkout/Type/Multishipping.php
Below function adds address to the quote item. You can manipulate $data parameter to accept multiple addresses via post request and make changes accordingly.
protected function _addShippingItem($quoteItemId, $data)
{
//sample logic
$quoteItem = $quote->getItemById($quoteItemId);
if($quoteItem) {
foreach($data['addresses'] as $address) {
$quoteAddress = $this->_addressFactory->create()->importCustomerAddressData($address);
$quote->addShippingAddress($quoteAddress);
$quoteAddress->addItem($quoteItem, $qty);
}
}
}
The quote item to address mappings are stored in the table 'quote_address_item'.
app/code/Vendor/Module/Model/Checkout/Type/Multishipping.php
/**
* Get quote addresses assigned to different quote items.
*
* Based on result array we can display each address separately
*
* @param int $itemId
* @return array
*/
public function getQuoteShippingAddressItemsByQuoteItemId($itemId)
{
$addressItems = [];
$addresses = $this->getQuote()->getAllShippingAddresses();
foreach ($addresses as $address) {
foreach ($address->getAllItems() as $item) {
if ($item->getParentItemId()) {
continue;
}
if ($item->getProduct()->getIsVirtual()) {
continue;
}
if($item->getQuoteItemId() == $itemId){
$addressItems[] = $item;
}
}
}
return $addressItems;
}
app/code/Vendor/Module/view/frontend/templates/checkout/addresses.phtml
<?php foreach ($block->getItems() as $_index => $_item) : ?>
<?php if ($_item->getQuoteItem()) : ?>
<tr>
<!-- other code -->
<!-- other code -->
<!-- other code -->
<td class="col address" data-th="<?= $block->escapeHtmlAttr(__('Send To')) ?>">
<?php if ($_item->getProduct()->getIsVirtual()) : ?>
<div class="applicable">
<?= $block->escapeHtml(__('A shipping selection is not applicable.')) ?>
</div>
<?php else : ?>
<div class="field address">
<label for="ship_<?= $block->escapeHtml($_index) ?>_<?= $block->escapeHtml($_item->getQuoteItemId()) ?>_address"
class="label">
<span><?= $block->escapeHtml(__('Send To')) ?></span>
</label>
<div class="control">
<?= $block->getAddressesHtmlSelect($_item, $_index) ?>
</div>
</div>
<?php endif; ?>
</td>
<!-- other code -->
<!-- other code -->
<!-- other code -->
</tr>
<?php endif; ?>
<?php endforeach; ?>
app/code/Vendor/Module/Block/Checkout/Addresses.php
/**
* @return array
*/
public function getItems()
{
$items = $this->getCheckout()->getQuote()->getAllVisibleItems();
return $items;
}
/**
* Get all shipping addresses by Item.
*
* @param int $itemId
* @return array
*/
public function getAllShippingAddressesByItem($itemId)
{
return $this->getCheckout()->getQuoteShippingAddressItemsByQuoteItemId($itemId);
}
/**
* Retrieve HTML for addresses dropdown
*
* @param mixed $item
* @param int $index
* @return string
*/
public function getAddressesHtmlSelect($itemId)
{
$addressItems = $this->getAllShippingAddressesByItem($itemId);
$index = 1;
$selectHtml = '';
$selectHtml .= '<div class="address-select">';
if(empty($addressItems)) {
$select = $this->getLayout()->createBlock(\Magento\Framework\View\Element\Html\Select::class)
->setName('ship[' . $itemId . '][address][' . $index . ']')
->setId('ship_' . $itemId . '_address_'. $index)
->setValue('')
->setOptions($this->getAddressOptions());
$selectHtml = $select->getHtml();
} else {
foreach ($addressItems as $addressItem) {
$select = $this->getLayout()->createBlock(\Magento\Framework\View\Element\Html\Select::class)
->setName('ship[' . $itemId . '][address][' . $index . ']')
->setId('ship_' . $itemId . '_address_'. $index)
->setValue($item->getCustomerAddressId())
->setOptions($this->getAddressOptions());
$selectHtml .= $select->getHtml();
$index++;
}
}
$selectHtml .= '</div>';
return $selectHtml;
}
/**
* Retrieve options for addresses dropdown
*
* @return array
*/
public function getAddressOptions()
{
$options = [];
$addresses = [];
$options[] = [
'value' => '',
'label' => 'Select an Address',
];
try {
$addresses = $this->customerRepository->getById($this->getCustomerId())->getAddresses();
} catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
/** Customer does not exist */
}
/** @var \Magento\Customer\Api\Data\AddressInterface $address */
foreach ($addresses as $address) {
$label = $this->_addressConfig
->getFormatByCode(AddressConfig::DEFAULT_ADDRESS_FORMAT)
->getRenderer()
->renderArray($this->addressMapper->toFlatArray($address));
$options[] = [
'value' => $address->getId(),
'label' => $label,
];
}
return $options;
}
Hope this helps to some extent.